From d6676b54b386effcafb62c9bbf06c43f0fbb8373 Mon Sep 17 00:00:00 2001 From: Josh Date: Wed, 23 Jun 2021 20:41:20 -0700 Subject: [PATCH 01/26] add pre-reqs and setup instructions --- sputnikdao2/README.md | 191 +++++++++++++++++++++++++++++++----------- 1 file changed, 144 insertions(+), 47 deletions(-) diff --git a/sputnikdao2/README.md b/sputnikdao2/README.md index 2d1ab21bb..8997cb6a0 100644 --- a/sputnikdao2/README.md +++ b/sputnikdao2/README.md @@ -1,91 +1,182 @@ # Sputnik DAO v2 -## Proposals +## Overview -Proposals is the main way to interact with the DAO. -Each action on the DAO is done by creating and approving proposal. +| Name | Description | +| ------------------- | --------------------------------------------------------- | +| [**Setup**](#setup) | Step-by-step guide to deploy DAO factory and DAO contract | -## Token voting +--- -DAO votes to select some token to become voting token (only can be done once, can't change later). +## Prerequisites -User flow is next: -- User's deposit the token into the DAO. -- They can then choose who to delegate these tokens. It can be to themself or to other users to increase their vote weight. -- When users vote for proposals, their vote is weighted by all the delegations to them. -- Undelegating will block delegating / withdrawing until one voting period passes. -- Undelegated tokens can be withdrawn by the user. +1. [NEAR Account](https://wallet.testnet.near.org) +2. [NEAR-CLI](https://docs.near.org/docs/tools/near-cli#setup) +3. [Rust](https://www.rust-lang.org) -## Bounties +> If you have not previously installed Rust as well as configured your current shell and WASM target please perform the following 3-Step Rust Installation: -The lifecycle of a bounty is the next: +1) Install Rustup: - - Anyone with permission can add proposal `AddBounty` which contains the bounty information, including `token` to pay the reward in and `amount` to pay it out. - - This proposal gets voted in by the current voting policy - - After proposal passed, the bounty get added. Now it has an `id` in the bounty list. Which can be queries via `get_bounties` - - Anyone can claim a bounty by calling `bounty_claim(id, deadline)` up to `repeat` times which was specified in the bounty. This allows to have repeatative bounties or multiple working collaboratively. `deadline` specifies how long it will take the sender to complete the bounty. - - If claimer decides to give up, they can call `bounty_giveup(id)`, and within `forgiveness_period` their claim bond will be returned. After this period, their bond is kept in the DAO. - - When bounty is complete, call `bounty_done(id)`, which will start add a proposal `BountyDone` that when voted will pay to whoever done the bounty. +``` +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +``` +[_(Taken from official installation guide)_](https://www.rust-lang.org/tools/install) -## Blob storage +2) Configure your current shell: -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. +``` +source $HOME/.cargo/env +``` -Blob lifecycle: - - Store blob in the DAO - - Create upgradability proposal - - Proposal passes or fails - - Remove blob and receive funds locked for storage back +3) Add Wasm target to your toolchain: -Blob can be removed only by the original storer. +``` +rustup target add wasm32-unknown-unknown +``` -## Testing +--- -To test the sputnik2 DAO you will need a testnet account. If you don't have one yet create it in https://wallet.testnet.near.org/. +## Setup -Lets assume your account is 'sputnik2.testnet', and you want to deploy your first DAO in 'genesis.sputnik2.testnet' +### 1. Login with your account: + +Using [`near-cli`](https://docs.near.org/docs/tools/near-cli#near-login), login to your account which will save your credentials locally: -#### Step 1. Login with your account: ``` near login ``` -#### Step 2. Deploy factory: +### 2. Clone repository: -Use `export CONTRACT_ID=sputnik2.testnet` in the terminal to set the account where to deploy the factory. Then, execute the following command from the root of this repository. +``` +git clone https://github.com/near-daos/sputnik-dao-contract +``` + +### 3. Build factory contract: ``` -near deploy $CONTRACT_ID --wasmFile=sputnikdao_factory2/res/sputnikdao_factory2.wasm +cd sputnik-dao-contract/sputnikdao-factory2 && ./build.sh ``` -#### Step 3. Initialize factory: +### 4. Deploy factory: + +Use `export CONTRACT_ID=YOUR_ACCOUNT.testnet` in the terminal to set the account ID where the factory contract will be deployed. Then, execute the following command from your current directory `sputnik-dao-contract/sputnikdao-factory2`: + +``` +near deploy $CONTRACT_ID --wasmFile=res/sputnikdao_factory2.wasm --accountId $CONTRACT_ID +``` + +### 5. Initialize factory: + ``` near call $CONTRACT_ID new --accountId $CONTRACT_ID ``` -#### Step 4. Define the parameters of the new DAO, its council and create it: +### 6. Define the parameters of the new DAO, its council, and create it: + +a) Define the council of your DAO: + +``` +export COUNCIL='["council-member.testnet", $CONTRACT_ID]' +``` -Define the council of your DAO: `export COUNCIL='["councilmember.testnet", "sputnik2.testnet"]'` +b) Configure the name, purpose, and initial council members of the DAO: ``` -# bash -ARGS=`echo '{"config": {"name": "genesis", "symbol": "GENESIS", "decimals": 24, "purpose": "test", "bond": "1000000000000000000000000", "metadata": ""}, "policy": '$COUNCIL'}' | base64 -w 0` +export ARGS=`echo '{"config": {"name": "genesis", "purpose": "Genesis DAO", "metadata":""}, "policy": ["council_member_1.testnet","council_member_2.testnet"]}' | base64` +``` + +c) Create the new DAO!: -# fish -set ARGS (echo '{"config": {"name": "genesis", "symbol": "GENESIS", "decimals": 24, "purpose": "test", "bond": "1000000000000000000000000", "metadata": ""}, "policy": '$COUNCIL'}' | base64 -w 0) +``` +near call $CONTRACT_ID create "{\"name\": \"genesis\", \"args\": \"$ARGS\"}" --accountId $CONTRACT_ID --amount 5 --gas 150000000000000 +``` -# Create a new DAO with the given parameters. -near call $CONTRACT_ID create "{\"name\": \"genesis\", \"args\": \"$ARGS\"}" --accountId $CONTRACT_ID --amount 5 --gas 150000000000000 +This will create a DAO with the following default values: + +```json + +{ + name: 'genesis', + purpose: 'Genesis DAO', + metadata: '' +} +{ + roles: [ + { + name: 'all', + kind: 'Everyone', + permissions: [ '*:AddProposal' ], + vote_policy: {} + }, + { + name: 'council', + kind: { Group: [ 'council_member_1.testnet', 'council_member_2.testnet' ] }, + permissions: [ '*:*' ], + vote_policy: {} + } + ], + default_vote_policy: { weight_kind: 'RoleWeight', threshold: [ 1, 2 ] }, + proposal_bond: '1000000000000000000000000', + proposal_period: '604800000000000', + bounty_bond: '1000000000000000000000000', + bounty_forgiveness_period: '86400000000000' +} ``` +--- + Set `export SPUTNIK_ID=genesis.$CONTRACT_ID`. Validate that it went through, and that it correctly set the policy: + ``` near view $SPUTNIK_ID get_policy ``` +## Proposals + +Proposals is the main way to interact with the DAO. +Each action on the DAO is done by creating and approving proposal. + +## Token voting + +DAO votes to select some token to become voting token (only can be done once, can't change later). + +User flow is next: + +- User's deposit the token into the DAO. +- They can then choose who to delegate these tokens. It can be to themself or to other users to increase their vote weight. +- When users vote for proposals, their vote is weighted by all the delegations to them. +- Undelegating will block delegating / withdrawing until one voting period passes. +- Undelegated tokens can be withdrawn by the user. + +## Bounties + +The lifecycle of a bounty is the next: + +- Anyone with permission can add proposal `AddBounty` which contains the bounty information, including `token` to pay the reward in and `amount` to pay it out. +- This proposal gets voted in by the current voting policy +- After proposal passed, the bounty get added. Now it has an `id` in the bounty list. Which can be queries via `get_bounties` +- Anyone can claim a bounty by calling `bounty_claim(id, deadline)` up to `repeat` times which was specified in the bounty. This allows to have repeatative bounties or multiple working collaboratively. `deadline` specifies how long it will take the sender to complete the bounty. +- If claimer decides to give up, they can call `bounty_giveup(id)`, and within `forgiveness_period` their claim bond will be returned. After this period, their bond is kept in the DAO. +- When bounty is complete, call `bounty_done(id)`, which will start add a proposal `BountyDone` that when voted will pay to whoever done the bounty. + +## Blob storage + +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: + +- Store blob in the DAO +- Create upgradability proposal +- Proposal passes or fails +- Remove blob and receive funds locked for storage back + +Blob can be removed only by the original storer. + #### Step 5. Create a proposal and interact with it: Lets use a third user, called `another-account.testnet` to create a proposal. The proposal asks for `another-account.testnet` they joins the council. The proposal will be votable for only a minute (`"submission_time":"60000000000"`). @@ -95,30 +186,34 @@ near call $SPUTNIK_ID add_proposal '{"proposal": {"description": "test", "submis ``` Vote "Approve" using the **council members**: + ``` near call $SPUTNIK_ID act_proposal '{"id": 0, "action": "VoteApprove"}' --accountId sputnik2.testnet -near call $SPUTNIK_ID act_proposal '{"id": 0, "action": "VoteApprove"}' --accountId councilmember.testnet +near call $SPUTNIK_ID act_proposal '{"id": 0, "action": "VoteApprove"}' --accountId councilmember.testnet ``` View proposal: + ``` near view $SPUTNIK_ID get_proposal '{"id": 0}' ``` After one minute, the user "another-account.testnet" will be added to the council + ``` near view $SPUTNIK_ID get_policy ``` View first 10 proposals: + ``` near view $SPUTNIK_ID get_proposals '{"from_index": 0, "limit": 10}' ``` - ## Proposal Kinds Each kind of proposal represents an operation the DAO can perform. Proposal kinds are: + ``` ProposalKind::ChangeConfig { .. } => "config", ProposalKind::ChangePolicy { .. } => "policy", @@ -134,6 +229,7 @@ ProposalKind::AddBounty { .. } => "add_bounty", ProposalKind::BountyDone { .. } => "bounty_done", ProposalKind::Vote => "vote", ``` + ### Voting Policy You can set a different vote policy for each one of the proposal kinds. @@ -151,6 +247,7 @@ When vote policy is `RoleWeight(role)`, vote % is measured against the count of The DAO can have several roles, and you can define permissions for each role. A permission is a combination of `proposal_kind:VotingAction` so they can become very specific. Actions are: + ``` /// Action to add proposal. Used internally. AddProposal, @@ -169,7 +266,7 @@ Finalize, MoveToHub ``` -so, for example a role with: `["mint:VoteReject","mint:VoteRemove"]` means the users with that role can only vote to *reject or remove a mint proposal*, but they can't vote to approve. +so, for example a role with: `["mint:VoteReject","mint:VoteRemove"]` means the users with that role can only vote to _reject or remove a mint proposal_, but they can't vote to approve. You can use `*` as a wildcard, so for example a role with `mint:*` can perform any vote action on mint proposals. From ace04c22350a476c839588b5658b56edc2e22201 Mon Sep 17 00:00:00 2001 From: Josh Date: Wed, 23 Jun 2021 22:09:05 -0700 Subject: [PATCH 02/26] add verification steps / example responses --- sputnikdao2/README.md | 82 ++++++++++++++++++++++++++++++------------- 1 file changed, 58 insertions(+), 24 deletions(-) diff --git a/sputnikdao2/README.md b/sputnikdao2/README.md index 8997cb6a0..30063a618 100644 --- a/sputnikdao2/README.md +++ b/sputnikdao2/README.md @@ -1,5 +1,8 @@ # Sputnik DAO v2 +// TODO: add short intro blurb +> Sputnik DAO v2 ... + ## Overview | Name | Description | @@ -61,7 +64,13 @@ cd sputnik-dao-contract/sputnikdao-factory2 && ./build.sh ### 4. Deploy factory: -Use `export CONTRACT_ID=YOUR_ACCOUNT.testnet` in the terminal to set the account ID where the factory contract will be deployed. Then, execute the following command from your current directory `sputnik-dao-contract/sputnikdao-factory2`: +- Create an env variable replacing `YOUR_ACCOUNT.testnet` with the name of the account you logged in with earlier: + +``` +export CONTRACT_ID=YOUR_ACCOUNT.testnet +``` + +- Deploy factory contract by running the following command from your current directory _(`sputnik-dao-contract/sputnikdao-factory2`)_: ``` near deploy $CONTRACT_ID --wasmFile=res/sputnikdao_factory2.wasm --accountId $CONTRACT_ID @@ -75,33 +84,60 @@ near call $CONTRACT_ID new --accountId $CONTRACT_ID ### 6. Define the parameters of the new DAO, its council, and create it: -a) Define the council of your DAO: +- Define the council of your DAO: ``` -export COUNCIL='["council-member.testnet", $CONTRACT_ID]' +export COUNCIL='["council-member.testnet", "YOUR_ACCOUNT.testnet"]' ``` -b) Configure the name, purpose, and initial council members of the DAO: +- Configure the name, purpose, and initial council members of the DAO and convert the arguments in base64: ``` -export ARGS=`echo '{"config": {"name": "genesis", "purpose": "Genesis DAO", "metadata":""}, "policy": ["council_member_1.testnet","council_member_2.testnet"]}' | base64` +export ARGS=`echo '{"config": {"name": "genesis", "purpose": "Genesis DAO", "metadata":""}, "policy": '$COUNCIL'}' | base64` ``` -c) Create the new DAO!: +- Create the new DAO!: ``` near call $CONTRACT_ID create "{\"name\": \"genesis\", \"args\": \"$ARGS\"}" --accountId $CONTRACT_ID --amount 5 --gas 150000000000000 ``` -This will create a DAO with the following default values: +
+Example Response: +

-```json +```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. -{ - name: 'genesis', - purpose: 'Genesis DAO', - metadata: '' -} +

+
+ +### 7. Verify successful deployment and policy configuration: + +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: + +``` +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) + +``` +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: [ { @@ -112,12 +148,18 @@ This will create a DAO with the following default values: }, { name: 'council', - kind: { Group: [ 'council_member_1.testnet', 'council_member_2.testnet' ] }, - permissions: [ '*:*' ], + kind: { Group: [ 'council-member.testnet', 'YOUR_ACCOUNT.testnet' ] }, + permissions: [ + '*:Finalize', + '*:AddProposal', + '*:VoteApprove', + '*:VoteReject', + '*:VoteRemove' + ], vote_policy: {} } ], - default_vote_policy: { weight_kind: 'RoleWeight', threshold: [ 1, 2 ] }, + default_vote_policy: { weight_kind: 'RoleWeight', quorum: '0', threshold: [ 1, 2 ] }, proposal_bond: '1000000000000000000000000', proposal_period: '604800000000000', bounty_bond: '1000000000000000000000000', @@ -127,14 +169,6 @@ This will create a DAO with the following default values: --- -Set `export SPUTNIK_ID=genesis.$CONTRACT_ID`. - -Validate that it went through, and that it correctly set the policy: - -``` -near view $SPUTNIK_ID get_policy -``` - ## Proposals Proposals is the main way to interact with the DAO. From ae7e4c3d173b4887aace0ea6578da97f8b5a55a2 Mon Sep 17 00:00:00 2001 From: Josh Date: Wed, 23 Jun 2021 22:18:35 -0700 Subject: [PATCH 03/26] collapse setup steps and rust install --- sputnikdao2/README.md | 58 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 47 insertions(+), 11 deletions(-) diff --git a/sputnikdao2/README.md b/sputnikdao2/README.md index 30063a618..7bf4fe8b3 100644 --- a/sputnikdao2/README.md +++ b/sputnikdao2/README.md @@ -17,7 +17,9 @@ 2. [NEAR-CLI](https://docs.near.org/docs/tools/near-cli#setup) 3. [Rust](https://www.rust-lang.org) -> If you have not previously installed Rust as well as configured your current shell and WASM target please perform the following 3-Step Rust Installation: +
+3-Step Rust Installation. +

1) Install Rustup: @@ -38,11 +40,16 @@ source $HOME/.cargo/env rustup target add wasm32-unknown-unknown ``` +

+
+ --- ## Setup -### 1. Login with your account: +
+1. Login with your account. +

Using [`near-cli`](https://docs.near.org/docs/tools/near-cli#near-login), login to your account which will save your credentials locally: @@ -50,19 +57,35 @@ Using [`near-cli`](https://docs.near.org/docs/tools/near-cli#near-login), login near login ``` -### 2. Clone repository: +

+
+ +
+2. Clone repository. +

``` git clone https://github.com/near-daos/sputnik-dao-contract ``` -### 3. Build factory contract: +

+
+ + +
+3. Build factory contract. +

``` cd sputnik-dao-contract/sputnikdao-factory2 && ./build.sh ``` -### 4. Deploy factory: +

+
+ +
+4. Deploy factory. +

- Create an env variable replacing `YOUR_ACCOUNT.testnet` with the name of the account you logged in with earlier: @@ -76,13 +99,23 @@ export CONTRACT_ID=YOUR_ACCOUNT.testnet near deploy $CONTRACT_ID --wasmFile=res/sputnikdao_factory2.wasm --accountId $CONTRACT_ID ``` -### 5. Initialize factory: +

+
+ +
+5. Initialize factory. +

``` near call $CONTRACT_ID new --accountId $CONTRACT_ID ``` -### 6. Define the parameters of the new DAO, its council, and create it: +

+
+ +
+6. Define the parameters of the new DAO, its council, and create it. +

- Define the council of your DAO: @@ -102,9 +135,7 @@ export ARGS=`echo '{"config": {"name": "genesis", "purpose": "Genesis DAO", "met near call $CONTRACT_ID create "{\"name\": \"genesis\", \"args\": \"$ARGS\"}" --accountId $CONTRACT_ID --amount 5 --gas 150000000000000 ``` -

-Example Response: -

+**Example Response:** ```bash Scheduling a call: sputnik-v2.testnet.create({"name": "genesis", "args": "eyJjb25maWciOiB7Im5hbWUiOiAiZ2VuZXNpcyIsICJwdXJwb3NlIjogIkdlbmVzaXMgREFPIiwgIm1ldGFkYXRhIjoiIn0sICJwb2xpY3kiOiBbImNvdW5jaWwtbWVtYmVyLnRlc3RuZXQiLCAiWU9VUl9BQ0NPVU5ULnRlc3RuZXQiXX0K"}) with attached 5 NEAR @@ -118,7 +149,9 @@ true

-### 7. Verify successful deployment and policy configuration: +
+7. Verify successful deployment and policy configuration. +

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. @@ -167,6 +200,9 @@ near view $SPUTNIK_ID get_policy } ``` +

+
+ --- ## Proposals From ca291cff00a48d2f38147e900241aaa80fb23bd1 Mon Sep 17 00:00:00 2001 From: Josh Date: Wed, 23 Jun 2021 22:41:40 -0700 Subject: [PATCH 04/26] organize categories / add to overview table --- sputnikdao2/README.md | 211 ++++++++++++++++++++++++------------------ 1 file changed, 120 insertions(+), 91 deletions(-) diff --git a/sputnikdao2/README.md b/sputnikdao2/README.md index 7bf4fe8b3..1942e8656 100644 --- a/sputnikdao2/README.md +++ b/sputnikdao2/README.md @@ -1,13 +1,21 @@ # Sputnik DAO v2 // TODO: add short intro blurb + > Sputnik DAO v2 ... ## Overview -| Name | Description | -| ------------------- | --------------------------------------------------------- | -| [**Setup**](#setup) | Step-by-step guide to deploy DAO factory and DAO contract | +| Name | Description | +| ----------------------------- | --------------------------------------------------------- | +| [Setup](#setup) | Step-by-step guide to deploy DAO factory and DAO contract | +|[Roles & Permissions](#roles-and-permissions)|| +| [Proposals](#proposals) | | +|[Voting Policy](#voting-policy)|| +| [Token Voting](#token-voting) | | +| [Bounties](#bounties) | | +| [Blob Storage](#blob-storage) | | +| [Examples](#examples) | | --- @@ -21,20 +29,21 @@ 3-Step Rust Installation.

-1) Install Rustup: +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: +2. Configure your current shell: ``` source $HOME/.cargo/env ``` -3) Add Wasm target to your toolchain: +3. Add Wasm target to your toolchain: ``` rustup target add wasm32-unknown-unknown @@ -71,7 +80,6 @@ git clone https://github.com/near-daos/sputnik-dao-contract

-
3. Build factory contract.

@@ -117,7 +125,7 @@ near call $CONTRACT_ID new --accountId $CONTRACT_ID

6. Define the parameters of the new DAO, its council, and create it.

-- Define the council of your DAO: +- Define the council of your DAO: ``` export COUNCIL='["council-member.testnet", "YOUR_ACCOUNT.testnet"]' @@ -144,6 +152,7 @@ To see the transaction in the transaction explorer, please open this url in your 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.

@@ -153,7 +162,7 @@ true 7. Verify successful deployment and policy configuration.

-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. +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: @@ -169,34 +178,37 @@ 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: [ + "roles": [ { - name: 'all', - kind: 'Everyone', - permissions: [ '*:AddProposal' ], - vote_policy: {} + "name": "all", + "kind": "Everyone", + "permissions": ["*:AddProposal"], + "vote_policy": {} }, { - name: 'council', - kind: { Group: [ 'council-member.testnet', 'YOUR_ACCOUNT.testnet' ] }, - permissions: [ - '*:Finalize', - '*:AddProposal', - '*:VoteApprove', - '*:VoteReject', - '*:VoteRemove' + "name": "council", + "kind": { "Group": ["council-member.testnet", "YOUR_ACCOUNT.testnet"] }, + "permissions": [ + "*:Finalize", + "*:AddProposal", + "*:VoteApprove", + "*:VoteReject", + "*:VoteRemove" ], - vote_policy: {} + "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' + "default_vote_policy": { + "weight_kind": "RoleWeight", + "quorum": "0", + "threshold": [1, 2] + }, + "proposal_bond": "1000000000000000000000000", + "proposal_period": "604800000000000", + "bounty_bond": "1000000000000000000000000", + "bounty_forgiveness_period": "86400000000000" } ``` @@ -205,11 +217,81 @@ near view $SPUTNIK_ID get_policy --- + +## Roles and Permissions + +The DAO can have several roles, and you can define permissions for each role. A permission is a combination of `proposal_kind:VotingAction` so they can become very specific. + +Actions are: + +``` +/// Action to add proposal. Used internally. +AddProposal, +/// Action to remove given proposal. Used for immediate deletion in special cases. +RemoveProposal, +/// Vote to approve given proposal or bounty. +VoteApprove, +/// Vote to reject given proposal or bounty. +VoteReject, +/// Vote to remove given proposal or bounty (because it's spam). +VoteRemove, +/// Finalize proposal, called when it's expired to return the funds +/// (or in the future can be used for early proposal closure). +Finalize, +/// Move a proposal to the hub to shift into another DAO. +MoveToHub +``` + +- For example, a role with: `["mint:VoteReject","mint:VoteRemove"]` means the users with that role can only vote to _reject or remove a mint proposal_, but they can't vote to approve. + +- You can use `*` as a wildcard, so for example a role with `mint:*` can perform any vote action on mint proposals. + +- You can also use `*:*` for unlimited permission, normally the `council` role has `*:*` as its configured permission so they can perform any vote action on any kind of proposal. + +--- + ## Proposals Proposals is the main way to interact with the DAO. Each action on the DAO is done by creating and approving proposal. + +### Proposal Kinds + +Each kind of proposal represents an operation the DAO can perform. Proposal kinds are: + +``` +ProposalKind::ChangeConfig { .. } => "config", +ProposalKind::ChangePolicy { .. } => "policy", +ProposalKind::AddMemberToRole { .. } => "add_member_to_role", +ProposalKind::RemoveMemberFromRole { .. } => "remove_member_from_role", +ProposalKind::FunctionCall { .. } => "call", +ProposalKind::UpgradeSelf { .. } => "upgrade_self", +ProposalKind::UpgradeRemote { .. } => "upgrade_remote", +ProposalKind::Transfer { .. } => "transfer", +ProposalKind::Mint { .. } => "mint", +ProposalKind::Burn { .. } => "burn", +ProposalKind::AddBounty { .. } => "add_bounty", +ProposalKind::BountyDone { .. } => "bounty_done", +ProposalKind::Vote => "vote", +``` + +--- + +## Voting Policy + +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 rol, 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. + +--- + ## Token voting DAO votes to select some token to become voting token (only can be done once, can't change later). @@ -222,6 +304,8 @@ User flow is next: - Undelegating will block delegating / withdrawing until one voting period passes. - Undelegated tokens can be withdrawn by the user. +--- + ## Bounties The lifecycle of a bounty is the next: @@ -233,6 +317,8 @@ The lifecycle of a bounty is the next: - If claimer decides to give up, they can call `bounty_giveup(id)`, and within `forgiveness_period` their claim bond will be returned. After this period, their bond is kept in the DAO. - When bounty is complete, call `bounty_done(id)`, which will start add a proposal `BountyDone` that when voted will pay to whoever done the bounty. +--- + ## Blob storage DAO supports storing larger blobs of data and content indexing them by hash of the data. @@ -247,6 +333,11 @@ Blob lifecycle: Blob can be removed only by the original storer. +--- + +## Examples + + #### Step 5. Create a proposal and interact with it: Lets use a third user, called `another-account.testnet` to create a proposal. The proposal asks for `another-account.testnet` they joins the council. The proposal will be votable for only a minute (`"submission_time":"60000000000"`). @@ -279,65 +370,3 @@ View first 10 proposals: ``` near view $SPUTNIK_ID get_proposals '{"from_index": 0, "limit": 10}' ``` - -## Proposal Kinds - -Each kind of proposal represents an operation the DAO can perform. Proposal kinds are: - -``` -ProposalKind::ChangeConfig { .. } => "config", -ProposalKind::ChangePolicy { .. } => "policy", -ProposalKind::AddMemberToRole { .. } => "add_member_to_role", -ProposalKind::RemoveMemberFromRole { .. } => "remove_member_from_role", -ProposalKind::FunctionCall { .. } => "call", -ProposalKind::UpgradeSelf { .. } => "upgrade_self", -ProposalKind::UpgradeRemote { .. } => "upgrade_remote", -ProposalKind::Transfer { .. } => "transfer", -ProposalKind::Mint { .. } => "mint", -ProposalKind::Burn { .. } => "burn", -ProposalKind::AddBounty { .. } => "add_bounty", -ProposalKind::BountyDone { .. } => "bounty_done", -ProposalKind::Vote => "vote", -``` - -### Voting Policy - -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 rol, 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. - -## Roles & Permissions - -The DAO can have several roles, and you can define permissions for each role. A permission is a combination of `proposal_kind:VotingAction` so they can become very specific. - -Actions are: - -``` -/// Action to add proposal. Used internally. -AddProposal, -/// Action to remove given proposal. Used for immediate deletion in special cases. -RemoveProposal, -/// Vote to approve given proposal or bounty. -VoteApprove, -/// Vote to reject given proposal or bounty. -VoteReject, -/// Vote to remove given proposal or bounty (because it's spam). -VoteRemove, -/// Finalize proposal, called when it's expired to return the funds -/// (or in the future can be used for early proposal closure). -Finalize, -/// Move a proposal to the hub to shift into another DAO. -MoveToHub -``` - -so, for example a role with: `["mint:VoteReject","mint:VoteRemove"]` means the users with that role can only vote to _reject or remove a mint proposal_, but they can't vote to approve. - -You can use `*` as a wildcard, so for example a role with `mint:*` can perform any vote action on mint proposals. - -You can also use `*:*` for unlimited permission, normally the `council` role has `*:*` as its configured permission so they can perform any vote action on any kind of proposal. From 6ed0cb2f450c1f422fe78aecfeb2e783749fb6ad Mon Sep 17 00:00:00 2001 From: Josh Date: Wed, 23 Jun 2021 22:56:16 -0700 Subject: [PATCH 05/26] copy edits / grammar --- sputnikdao2/README.md | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/sputnikdao2/README.md b/sputnikdao2/README.md index 1942e8656..df59a80b6 100644 --- a/sputnikdao2/README.md +++ b/sputnikdao2/README.md @@ -220,7 +220,7 @@ near view $SPUTNIK_ID get_policy ## Roles and Permissions -The DAO can have several roles, and you can define permissions for each role. A permission is a combination of `proposal_kind:VotingAction` so they can become very specific. +> The DAO can have several roles, and you can define permissions for each role. A permission is a combination of `proposal_kind:VotingAction` so they can become very specific. Actions are: @@ -252,13 +252,11 @@ MoveToHub ## Proposals -Proposals is the main way to interact with the DAO. -Each action on the DAO is done by creating and approving proposal. - +> Proposals are the main way to interact with the DAO. Each action on the DAO is done by creating and approving a proposal. ### Proposal Kinds -Each kind of proposal represents an operation the DAO can perform. Proposal kinds are: +Each kind of proposal represents an operation the DAO can perform. Here are the proposal options: ``` ProposalKind::ChangeConfig { .. } => "config", @@ -280,7 +278,7 @@ ProposalKind::Vote => "vote", ## Voting Policy -You can set a different vote policy for each one of the proposal kinds. +> 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. @@ -294,7 +292,7 @@ When vote policy is `RoleWeight(role)`, vote % is measured against the count of ## Token voting -DAO votes to select some token to become voting token (only can be done once, can't change later). +> DAO votes to select some token to become voting token (only can be done once, can't change later). User flow is next: @@ -308,28 +306,31 @@ User flow is next: ## Bounties +// TODO: add bounty blurb +> Bounties... (add blurb) + The lifecycle of a bounty is the next: -- Anyone with permission can add proposal `AddBounty` which contains the bounty information, including `token` to pay the reward in and `amount` to pay it out. -- This proposal gets voted in by the current voting policy -- After proposal passed, the bounty get added. Now it has an `id` in the bounty list. Which can be queries via `get_bounties` -- Anyone can claim a bounty by calling `bounty_claim(id, deadline)` up to `repeat` times which was specified in the bounty. This allows to have repeatative bounties or multiple working collaboratively. `deadline` specifies how long it will take the sender to complete the bounty. -- If claimer decides to give up, they can call `bounty_giveup(id)`, and within `forgiveness_period` their claim bond will be returned. After this period, their bond is kept in the DAO. -- When bounty is complete, call `bounty_done(id)`, which will start add a proposal `BountyDone` that when voted will pay to whoever done the bounty. +- Anyone with permission can add proposal `AddBounty` which contains the bounty information including `token` to pay the reward in and `amount` to pay it out. +- This proposal gets voted in by the current voting policy. +- After proposal is passed, the bounty gets added. Now it has an `id` in the bounty list which can be queried via `get_bounties`. +- Anyone can claim a bounty by calling `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. +- If claimer decides to give up, they can call `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. +- When a bounty is complete, call `bounty_done(id)`, which will add a proposal `BountyDone` that, when voted, will pay to whoever completed the bounty. --- ## Blob storage -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. +> 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: -- Store blob in the DAO -- Create upgradability proposal -- Proposal passes or fails -- Remove blob and receive funds locked for storage back +- Store blob in the DAO. +- Create upgradability proposal. +- Proposal passes or fails. +- Remove blob and receive funds locked for storage back. Blob can be removed only by the original storer. From 120ef76cea3652609cb3b1d1095d26f3caae505c Mon Sep 17 00:00:00 2001 From: Josh Date: Thu, 24 Jun 2021 09:40:02 -0700 Subject: [PATCH 06/26] mrelocate examples --- sputnikdao2/README.md | 80 ++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 50 deletions(-) diff --git a/sputnikdao2/README.md b/sputnikdao2/README.md index df59a80b6..9d0afa7ba 100644 --- a/sputnikdao2/README.md +++ b/sputnikdao2/README.md @@ -1,21 +1,19 @@ # Sputnik DAO v2 -// TODO: add short intro blurb - > Sputnik DAO v2 ... ## Overview -| Name | Description | -| ----------------------------- | --------------------------------------------------------- | -| [Setup](#setup) | Step-by-step guide to deploy DAO factory and DAO contract | -|[Roles & Permissions](#roles-and-permissions)|| -| [Proposals](#proposals) | | -|[Voting Policy](#voting-policy)|| -| [Token Voting](#token-voting) | | -| [Bounties](#bounties) | | -| [Blob Storage](#blob-storage) | | -| [Examples](#examples) | | +| Name | Description | +| ----------------------------------------------- | --------------------------------------------------------- | +| [Setup](#setup) | Step-by-step guide to deploy DAO factory and DAO contract | +| [Roles & Permissions](#roles-and-permissions) | | +| [Proposals](#proposals) | | +| [Voting Policy](#voting-policy) | | +| [Token Voting](#token-voting) | | +| [Bounties](#bounties) | | +| [Blob Storage](#blob-storage) | | +| [Examples](../examples/sputnikdao2-examples.md) | | --- @@ -217,7 +215,6 @@ near view $SPUTNIK_ID get_policy --- - ## Roles and Permissions > The DAO can have several roles, and you can define permissions for each role. A permission is a combination of `proposal_kind:VotingAction` so they can become very specific. @@ -274,6 +271,24 @@ ProposalKind::BountyDone { .. } => "bounty_done", ProposalKind::Vote => "vote", ``` +### Add a proposal + +- By default, anyone can add a proposal. Use the following `near-cli` command to call `add_proposal` on your DAO contract. + +``` +near call YOUR_SPUTNIK_DAO_CONTRACT.testnet add_proposal '{"proposal": {"description": "Add New Council", "kind": {"AddMemberToRole": {"member_id": "council_member_3.testnet", "role": "council"}}}}' --accountId proposer.testnet --amount 1 +``` + +### Approve a proposal + +- Only council members can approve a proposal. Use the following `near-cli` command to call `act_proposal` on your DAO contract. + +``` +near call genesis.sputnik.testnet act_proposal '{"id": ID_from_previous_call, "action": "VoteApprove"}' --accountId council_member_1.testnet +``` + +record ID returned from this call. + --- ## Voting Policy @@ -307,6 +322,7 @@ User flow is next: ## Bounties // TODO: add bounty blurb + > Bounties... (add blurb) The lifecycle of a bounty is the next: @@ -314,7 +330,7 @@ The lifecycle of a bounty is the next: - Anyone with permission can add proposal `AddBounty` which contains the bounty information including `token` to pay the reward in and `amount` to pay it out. - This proposal gets voted in by the current voting policy. - After proposal is passed, the bounty gets added. Now it has an `id` in the bounty list which can be queried via `get_bounties`. -- Anyone can claim a bounty by calling `bounty_claim(id, deadline)` up to `repeat` times which was specified in the bounty. This allows to have repetitive bounties or multiple working collaboratively. +- Anyone can claim a bounty by calling `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. - If claimer decides to give up, they can call `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. - When a bounty is complete, call `bounty_done(id)`, which will add a proposal `BountyDone` that, when voted, will pay to whoever completed the bounty. @@ -335,39 +351,3 @@ Blob lifecycle: Blob can be removed only by the original storer. --- - -## Examples - - -#### Step 5. Create a proposal and interact with it: - -Lets use a third user, called `another-account.testnet` to create a proposal. The proposal asks for `another-account.testnet` they joins the council. The proposal will be votable for only a minute (`"submission_time":"60000000000"`). - -``` -near call $SPUTNIK_ID add_proposal '{"proposal": {"description": "test", "submission_time":"60000000000", "kind": {"AddMemberToRole": {"member_id": "another-account.testnet", "role": "council"}}}}' --accountId another-account.testnet --amount 1 -``` - -Vote "Approve" using the **council members**: - -``` -near call $SPUTNIK_ID act_proposal '{"id": 0, "action": "VoteApprove"}' --accountId sputnik2.testnet -near call $SPUTNIK_ID act_proposal '{"id": 0, "action": "VoteApprove"}' --accountId councilmember.testnet -``` - -View proposal: - -``` -near view $SPUTNIK_ID get_proposal '{"id": 0}' -``` - -After one minute, the user "another-account.testnet" will be added to the council - -``` -near view $SPUTNIK_ID get_policy -``` - -View first 10 proposals: - -``` -near view $SPUTNIK_ID get_proposals '{"from_index": 0, "limit": 10}' -``` From 806555a8165ff87232a1cac8fd2aa2932452b69f Mon Sep 17 00:00:00 2001 From: Josh Date: Thu, 24 Jun 2021 09:40:02 -0700 Subject: [PATCH 07/26] relocate examples --- sputnikdao2/README.md | 80 ++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 50 deletions(-) diff --git a/sputnikdao2/README.md b/sputnikdao2/README.md index df59a80b6..9d0afa7ba 100644 --- a/sputnikdao2/README.md +++ b/sputnikdao2/README.md @@ -1,21 +1,19 @@ # Sputnik DAO v2 -// TODO: add short intro blurb - > Sputnik DAO v2 ... ## Overview -| Name | Description | -| ----------------------------- | --------------------------------------------------------- | -| [Setup](#setup) | Step-by-step guide to deploy DAO factory and DAO contract | -|[Roles & Permissions](#roles-and-permissions)|| -| [Proposals](#proposals) | | -|[Voting Policy](#voting-policy)|| -| [Token Voting](#token-voting) | | -| [Bounties](#bounties) | | -| [Blob Storage](#blob-storage) | | -| [Examples](#examples) | | +| Name | Description | +| ----------------------------------------------- | --------------------------------------------------------- | +| [Setup](#setup) | Step-by-step guide to deploy DAO factory and DAO contract | +| [Roles & Permissions](#roles-and-permissions) | | +| [Proposals](#proposals) | | +| [Voting Policy](#voting-policy) | | +| [Token Voting](#token-voting) | | +| [Bounties](#bounties) | | +| [Blob Storage](#blob-storage) | | +| [Examples](../examples/sputnikdao2-examples.md) | | --- @@ -217,7 +215,6 @@ near view $SPUTNIK_ID get_policy --- - ## Roles and Permissions > The DAO can have several roles, and you can define permissions for each role. A permission is a combination of `proposal_kind:VotingAction` so they can become very specific. @@ -274,6 +271,24 @@ ProposalKind::BountyDone { .. } => "bounty_done", ProposalKind::Vote => "vote", ``` +### Add a proposal + +- By default, anyone can add a proposal. Use the following `near-cli` command to call `add_proposal` on your DAO contract. + +``` +near call YOUR_SPUTNIK_DAO_CONTRACT.testnet add_proposal '{"proposal": {"description": "Add New Council", "kind": {"AddMemberToRole": {"member_id": "council_member_3.testnet", "role": "council"}}}}' --accountId proposer.testnet --amount 1 +``` + +### Approve a proposal + +- Only council members can approve a proposal. Use the following `near-cli` command to call `act_proposal` on your DAO contract. + +``` +near call genesis.sputnik.testnet act_proposal '{"id": ID_from_previous_call, "action": "VoteApprove"}' --accountId council_member_1.testnet +``` + +record ID returned from this call. + --- ## Voting Policy @@ -307,6 +322,7 @@ User flow is next: ## Bounties // TODO: add bounty blurb + > Bounties... (add blurb) The lifecycle of a bounty is the next: @@ -314,7 +330,7 @@ The lifecycle of a bounty is the next: - Anyone with permission can add proposal `AddBounty` which contains the bounty information including `token` to pay the reward in and `amount` to pay it out. - This proposal gets voted in by the current voting policy. - After proposal is passed, the bounty gets added. Now it has an `id` in the bounty list which can be queried via `get_bounties`. -- Anyone can claim a bounty by calling `bounty_claim(id, deadline)` up to `repeat` times which was specified in the bounty. This allows to have repetitive bounties or multiple working collaboratively. +- Anyone can claim a bounty by calling `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. - If claimer decides to give up, they can call `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. - When a bounty is complete, call `bounty_done(id)`, which will add a proposal `BountyDone` that, when voted, will pay to whoever completed the bounty. @@ -335,39 +351,3 @@ Blob lifecycle: Blob can be removed only by the original storer. --- - -## Examples - - -#### Step 5. Create a proposal and interact with it: - -Lets use a third user, called `another-account.testnet` to create a proposal. The proposal asks for `another-account.testnet` they joins the council. The proposal will be votable for only a minute (`"submission_time":"60000000000"`). - -``` -near call $SPUTNIK_ID add_proposal '{"proposal": {"description": "test", "submission_time":"60000000000", "kind": {"AddMemberToRole": {"member_id": "another-account.testnet", "role": "council"}}}}' --accountId another-account.testnet --amount 1 -``` - -Vote "Approve" using the **council members**: - -``` -near call $SPUTNIK_ID act_proposal '{"id": 0, "action": "VoteApprove"}' --accountId sputnik2.testnet -near call $SPUTNIK_ID act_proposal '{"id": 0, "action": "VoteApprove"}' --accountId councilmember.testnet -``` - -View proposal: - -``` -near view $SPUTNIK_ID get_proposal '{"id": 0}' -``` - -After one minute, the user "another-account.testnet" will be added to the council - -``` -near view $SPUTNIK_ID get_policy -``` - -View first 10 proposals: - -``` -near view $SPUTNIK_ID get_proposals '{"from_index": 0, "limit": 10}' -``` From 8cc460e618aadf3ad98047ab55b71443a83620c9 Mon Sep 17 00:00:00 2001 From: Josh Date: Thu, 24 Jun 2021 14:28:55 -0700 Subject: [PATCH 08/26] add main intro and overivew descriptions --- sputnikdao2/README.md | 42 ++++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/sputnikdao2/README.md b/sputnikdao2/README.md index 9d0afa7ba..ecc19a3db 100644 --- a/sputnikdao2/README.md +++ b/sputnikdao2/README.md @@ -1,19 +1,19 @@ # Sputnik DAO v2 -> Sputnik DAO v2 ... +> Sputnik DAO v2 offers all of the functionality of v1 but with more features and enhanced configuration options. ## Overview -| Name | Description | -| ----------------------------------------------- | --------------------------------------------------------- | -| [Setup](#setup) | Step-by-step guide to deploy DAO factory and DAO contract | -| [Roles & Permissions](#roles-and-permissions) | | -| [Proposals](#proposals) | | -| [Voting Policy](#voting-policy) | | -| [Token Voting](#token-voting) | | -| [Bounties](#bounties) | | -| [Blob Storage](#blob-storage) | | -| [Examples](../examples/sputnikdao2-examples.md) | | +| Name | Description | +| --------------------------------------------- | --------------------------------------------------------------------- | +| [Setup](#setup) | Step-by-step guide to deploy a DAO factory and DAO contracts. | +| [Roles & Permissions](#roles-and-permissions) | Setup roles and define permissions for each role. | +| [Proposals](#proposals) | Each action on the DAO is done by creating and approving a proposal. | +| [Voting Policy](#voting-policy) | Configure voting policies for proposal types. | +| [Token Voting](#token-voting) | DAO can decide to use a governance token for voting purposes. | +| [Bounties](#bounties) | Add and configure bounties. | +| [Blob Storage](#blob-storage) | Store large data blobs and content and index them by the data's hash. | +| [Examples](./examples/README.md) | Examples and guides for interacting with Sputnik DAO v2. | --- @@ -271,20 +271,28 @@ ProposalKind::BountyDone { .. } => "bounty_done", ProposalKind::Vote => "vote", ``` -### Add a proposal +### Add proposal - By default, anyone can add a proposal. Use the following `near-cli` command to call `add_proposal` on your DAO contract. ``` -near call YOUR_SPUTNIK_DAO_CONTRACT.testnet add_proposal '{"proposal": {"description": "Add New Council", "kind": {"AddMemberToRole": {"member_id": "council_member_3.testnet", "role": "council"}}}}' --accountId proposer.testnet --amount 1 +near call YOUR_SPUTNIK_DAO_CONTRACT add_proposal '{"proposal": {"description": "Add New Council", "kind": {"AddMemberToRole": {"member_id": "council_member_3.testnet", "role": "council"}}}}' --accountId proposer.testnet --amount 1 ``` -### Approve a proposal +### Approve proposal - Only council members can approve a proposal. Use the following `near-cli` command to call `act_proposal` on your DAO contract. ``` -near call genesis.sputnik.testnet act_proposal '{"id": ID_from_previous_call, "action": "VoteApprove"}' --accountId council_member_1.testnet +near call YOUR_DAO_ACCOUNT act_proposal '{"id": ID_from_previous_call, "action": "VoteApprove"}' --accountId council_member_1.testnet +``` + +### View proposal + +View proposal: + +``` +near view YOUR_DAO_ACCOUNT get_proposal '{"id": 0}' ``` record ID returned from this call. @@ -321,9 +329,7 @@ User flow is next: ## Bounties -// TODO: add bounty blurb - -> Bounties... (add blurb) +> Add and configure bounties using `AddBounty` proposal. The lifecycle of a bounty is the next: From e037fdc0143dfac297321b82848782862eefb979 Mon Sep 17 00:00:00 2001 From: Josh Date: Thu, 24 Jun 2021 19:15:40 -0700 Subject: [PATCH 09/26] enhance roles and permissions section --- sputnikdao2/README.md | 53 +++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 24 deletions(-) diff --git a/sputnikdao2/README.md b/sputnikdao2/README.md index ecc19a3db..46f426bf4 100644 --- a/sputnikdao2/README.md +++ b/sputnikdao2/README.md @@ -1,12 +1,12 @@ # Sputnik DAO v2 -> Sputnik DAO v2 offers all of the functionality of v1 but with more features and enhanced configuration options. +> Building on the functionality of v1, Sputnik DAO v2 offers even more features and enhanced configuration ability. ## Overview | Name | Description | | --------------------------------------------- | --------------------------------------------------------------------- | -| [Setup](#setup) | Step-by-step guide to deploy a DAO factory and DAO contracts. | +| [Setup](#setup) | Step-by-step guide to deploy a DAO factory and DAO contracts. | | [Roles & Permissions](#roles-and-permissions) | Setup roles and define permissions for each role. | | [Proposals](#proposals) | Each action on the DAO is done by creating and approving a proposal. | | [Voting Policy](#voting-policy) | Configure voting policies for proposal types. | @@ -217,33 +217,26 @@ near view $SPUTNIK_ID get_policy ## Roles and Permissions -> The DAO can have several roles, and you can define permissions for each role. A permission is a combination of `proposal_kind:VotingAction` so they can become very specific. +> The DAO can have several roles, each of which allows for permission configuring. These permissions are a combination of [`proposal_kind`](#proposal-kinds) and `VotingAction`. Due to this combination these permissions can be scoped to be very specific or you can use wildcards to allow roles to have greater access. -Actions are: +Example: +- A role with: `["mint:VoteReject","mint:VoteRemove"]` means they can only vote to *reject* or *remove* a `mint` proposal but they can't vote to approve. -``` -/// Action to add proposal. Used internally. -AddProposal, -/// Action to remove given proposal. Used for immediate deletion in special cases. -RemoveProposal, -/// Vote to approve given proposal or bounty. -VoteApprove, -/// Vote to reject given proposal or bounty. -VoteReject, -/// Vote to remove given proposal or bounty (because it's spam). -VoteRemove, -/// Finalize proposal, called when it's expired to return the funds -/// (or in the future can be used for early proposal closure). -Finalize, -/// Move a proposal to the hub to shift into another DAO. -MoveToHub -``` +- A role with: `["mint:*"]` can perform any vote action on `mint` proposals. -- For example, a role with: `["mint:VoteReject","mint:VoteRemove"]` means the users with that role can only vote to _reject or remove a mint proposal_, but they can't vote to approve. +- 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. -- You can use `*` as a wildcard, so for example a role with `mint:*` can perform any vote action on mint proposals. +*Here is a list of all seven actions:* -- You can also use `*:*` for unlimited permission, normally the `council` role has `*:*` as its configured permission so they can perform any vote action on any kind of proposal. +| Action | Description | +| ---------------- | --------------------------------------------------------------------------------- | +| `AddProposal` | Adds proposal which is used internally. | +| `RemoveProposal` | Removes given proposal which 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 (because it's spam). | +| `Finalize` | Finalizes proposal which is canalled when proposal has expired and returns funds. | +| `MoveToHub` | Move a proposal to the hub to shift into another DAO. | --- @@ -256,6 +249,7 @@ MoveToHub Each kind of proposal represents an operation the DAO can perform. Here are the proposal options: ``` + ProposalKind::ChangeConfig { .. } => "config", ProposalKind::ChangePolicy { .. } => "policy", ProposalKind::AddMemberToRole { .. } => "add_member_to_role", @@ -269,6 +263,7 @@ ProposalKind::Burn { .. } => "burn", ProposalKind::AddBounty { .. } => "add_bounty", ProposalKind::BountyDone { .. } => "bounty_done", ProposalKind::Vote => "vote", + ``` ### Add proposal @@ -276,7 +271,9 @@ ProposalKind::Vote => "vote", - By default, anyone can add a proposal. Use the following `near-cli` command to call `add_proposal` on your DAO contract. ``` + near call YOUR_SPUTNIK_DAO_CONTRACT add_proposal '{"proposal": {"description": "Add New Council", "kind": {"AddMemberToRole": {"member_id": "council_member_3.testnet", "role": "council"}}}}' --accountId proposer.testnet --amount 1 + ``` ### Approve proposal @@ -284,7 +281,9 @@ near call YOUR_SPUTNIK_DAO_CONTRACT add_proposal '{"proposal": {"description": " - Only council members can approve a proposal. Use the following `near-cli` command to call `act_proposal` on your DAO contract. ``` + near call YOUR_DAO_ACCOUNT act_proposal '{"id": ID_from_previous_call, "action": "VoteApprove"}' --accountId council_member_1.testnet + ``` ### View proposal @@ -292,7 +291,9 @@ near call YOUR_DAO_ACCOUNT act_proposal '{"id": ID_from_previous_call, "action": View proposal: ``` + near view YOUR_DAO_ACCOUNT get_proposal '{"id": 0}' + ``` record ID returned from this call. @@ -357,3 +358,7 @@ Blob lifecycle: Blob can be removed only by the original storer. --- + +``` + +``` From b90e50521ea541c79b347bbe9618b6723d76e2da Mon Sep 17 00:00:00 2001 From: Josh Date: Thu, 24 Jun 2021 20:10:02 -0700 Subject: [PATCH 10/26] begin enhancing proposals section and consolidate voting --- sputnikdao2/README.md | 113 ++++++++++++++++++++++++++++-------------- 1 file changed, 76 insertions(+), 37 deletions(-) diff --git a/sputnikdao2/README.md b/sputnikdao2/README.md index 46f426bf4..a1b6e6b3a 100644 --- a/sputnikdao2/README.md +++ b/sputnikdao2/README.md @@ -9,8 +9,7 @@ | [Setup](#setup) | Step-by-step guide to deploy a DAO factory and DAO contracts. | | [Roles & Permissions](#roles-and-permissions) | Setup roles and define permissions for each role. | | [Proposals](#proposals) | Each action on the DAO is done by creating and approving a proposal. | -| [Voting Policy](#voting-policy) | Configure voting policies for proposal types. | -| [Token Voting](#token-voting) | DAO can decide to use a governance token for voting purposes. | +| [Voting](#voting) | Configure policies, setup governance tokens, and vote on proposals. | | [Bounties](#bounties) | Add and configure bounties. | | [Blob Storage](#blob-storage) | Store large data blobs and content and index them by the data's hash. | | [Examples](./examples/README.md) | Examples and guides for interacting with Sputnik DAO v2. | @@ -60,7 +59,7 @@ 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 ``` @@ -71,7 +70,7 @@ near login

2. Clone repository.

-``` +```bash git clone https://github.com/near-daos/sputnik-dao-contract ``` @@ -82,7 +81,7 @@ git clone https://github.com/near-daos/sputnik-dao-contract

3. Build factory contract.

-``` +```bash cd sputnik-dao-contract/sputnikdao-factory2 && ./build.sh ``` @@ -95,13 +94,13 @@ 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 ``` @@ -112,7 +111,7 @@ near deploy $CONTRACT_ID --wasmFile=res/sputnikdao_factory2.wasm --accountId $CO

5. Initialize factory.

-``` +```bash near call $CONTRACT_ID new --accountId $CONTRACT_ID ``` @@ -125,19 +124,19 @@ near call $CONTRACT_ID new --accountId $CONTRACT_ID - 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 5 --gas 150000000000000 ``` @@ -164,13 +163,13 @@ The DAO deployment will create a new [sub-account](https://docs.near.org/docs/co - 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 ``` @@ -219,14 +218,15 @@ near view $SPUTNIK_ID get_policy > The DAO can have several roles, each of which allows for permission configuring. These permissions are a combination of [`proposal_kind`](#proposal-kinds) and `VotingAction`. Due to this combination these permissions can be scoped to be very specific or you can use wildcards to allow roles to have greater access. -Example: -- A role with: `["mint:VoteReject","mint:VoteRemove"]` means they can only vote to *reject* or *remove* a `mint` proposal but they can't vote to approve. +_Example:_ + +- A role with: `["mint:VoteReject","mint:VoteRemove"]` means they can only vote to _reject_ or _remove_ a `mint` proposal but they can't vote to approve. - A role with: `["mint:*"]` can perform any vote action on `mint` 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. +- 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 all seven actions:* +**Here is a list of all seven actions:** | Action | Description | | ---------------- | --------------------------------------------------------------------------------- | @@ -242,14 +242,26 @@ Example: ## Proposals -> Proposals are the main way to interact with the DAO. Each action on the DAO is done by creating and approving a proposal. +> Proposals are the main way to interact with the DAO. Each action on the DAO is performed by creating and approving a proposal. -### Proposal Kinds +### Overview -Each kind of proposal represents an operation the DAO can perform. Here are the proposal options: +| Action | Description | +| ---------------------------------------------------- | ------------------------------------------------ | +| [Proposal Kinds](#proposal-kinds) | Types of proposals you can create on the DAO. | +| **METHODS** | | +| [`add_proposal`](#add-proposal) | Adds proposal to the DAO. | +| [`view_proposal`](#view-proposal) | Views proposal by ID. | +| [`get_num_proposals`](#get-proposal-count) | Gets total count of active proposals on the DAO. | +| [`get_proposals_by_status`](#get-proposal-by-status) | Gets all proposals by status. | +| [`approve_proposal`](#approve-proposal) | Approves the proposal. | +| [`finalize`](#finalize) | | -``` +### Proposal Kinds + +- Each kind of proposal represents an operation the DAO can perform. Here are the proposal options: +```rs ProposalKind::ChangeConfig { .. } => "config", ProposalKind::ChangePolicy { .. } => "policy", ProposalKind::AddMemberToRole { .. } => "add_member_to_role", @@ -263,50 +275,77 @@ ProposalKind::Burn { .. } => "burn", ProposalKind::AddBounty { .. } => "add_bounty", ProposalKind::BountyDone { .. } => "bounty_done", ProposalKind::Vote => "vote", - ``` ### Add proposal -- By default, anyone can add a proposal. Use the following `near-cli` command to call `add_proposal` on your DAO contract. +- By default, anyone can add a proposal. Adding a proposal is performed by calling `add_proposal` on your DAO contract. + +Example of ARG Structure: +```json +{ + "proposal": { + "description": "Add New Council", + "kind": { + "AddMemberToRole": { + "member_id": "council_member_3.testnet", + "role": "council" + } + } + } +} ``` -near call YOUR_SPUTNIK_DAO_CONTRACT add_proposal '{"proposal": {"description": "Add New Council", "kind": {"AddMemberToRole": {"member_id": "council_member_3.testnet", "role": "council"}}}}' --accountId proposer.testnet --amount 1 +**Example `near-cli` command:** +```bash +near call YOUR_SPUTNIK_DAO_CONTRACT add_proposal '{"proposal": {"description": "Add New Council", "kind": {"AddMemberToRole": {"member_id": "council_member_3.testnet", "role": "council"}}}}' --accountId proposer.testnet --amount 1 ``` -### Approve proposal +### View proposal -- Only council members can approve a proposal. Use the following `near-cli` command to call `act_proposal` on your DAO contract. +- Anyone can view a proposal by calling `get_proposal` and passing the `id` of the proposal they wish to view: +```bash +near view YOUR_DAO_ACCOUNT get_proposal '{"id": 0}' ``` -near call YOUR_DAO_ACCOUNT act_proposal '{"id": ID_from_previous_call, "action": "VoteApprove"}' --accountId council_member_1.testnet +### Get proposal count -``` +- Gets total number of proposals on DAO -### View proposal +### Get proposals by status -View proposal: +- Gets proposals by status or multiple statuses depending on method used. -``` +### Approve proposal -near view YOUR_DAO_ACCOUNT get_proposal '{"id": 0}' +- Only council members can approve a proposal. Approving a proposal is performed by calling `act_proposal` and passing an `id` and `action` with `VoteApprove` as the value. -``` +**Example:** -record ID returned from this call. +```bash +near call YOUR_DAO_ACCOUNT act_proposal '{"id": ID_from_previous_call, "action": "VoteApprove"}' --accountId council_member_1.testnet +``` --- -## Voting Policy +## Voting + +> + +### Voting on a proposal + +> Only council members are allowed to vote on a proposal. + +### Voting policy > 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 rol, and you can say that you need 1m tokens to approve a proposal disregarding total token supply. +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. @@ -314,7 +353,7 @@ When vote policy is `RoleWeight(role)`, vote % is measured against the count of --- -## Token voting +### Token voting > DAO votes to select some token to become voting token (only can be done once, can't change later). From 87ee535473c4cc93244ce2772252f3951cef7722 Mon Sep 17 00:00:00 2001 From: Josh Date: Thu, 24 Jun 2021 23:59:32 -0700 Subject: [PATCH 11/26] change overview to contents --- sputnikdao2/README.md | 56 ++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/sputnikdao2/README.md b/sputnikdao2/README.md index a1b6e6b3a..f44699e7b 100644 --- a/sputnikdao2/README.md +++ b/sputnikdao2/README.md @@ -216,7 +216,7 @@ near view $SPUTNIK_ID get_policy ## Roles and Permissions -> The DAO can have several roles, each of which allows for permission configuring. These permissions are a combination of [`proposal_kind`](#proposal-kinds) and `VotingAction`. Due to this combination these permissions can be scoped to be very specific or you can use wildcards to allow roles to have greater access. +> The DAO can have several roles, each of which allows for permission configuring. These permissions are a combination of [`proposal_kind`](#proposal-types) and `VotingAction`. Due to this combination these permissions can be scoped to be very specific or you can use wildcards to allow roles to have greater access. _Example:_ @@ -228,15 +228,13 @@ _Example:_ **Here is a list of all seven actions:** -| Action | Description | -| ---------------- | --------------------------------------------------------------------------------- | -| `AddProposal` | Adds proposal which is used internally. | -| `RemoveProposal` | Removes given proposal which 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 (because it's spam). | -| `Finalize` | Finalizes proposal which is canalled when proposal has expired and returns funds. | -| `MoveToHub` | Move a proposal to the hub to shift into another DAO. | +- `AddProposal` - _Adds proposal which is used internally._ +- `RemoveProposal` - _Removes given proposal which 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 (because it's spam)._ +- `Finalize` - _Finalizes proposal which is canalled when proposal has expired and returns funds._ +- `MoveToHub` - _Move a proposal to the hub to shift into another DAO._ --- @@ -244,22 +242,20 @@ _Example:_ > Proposals are the main way to interact with the DAO. Each action on the DAO is performed by creating and approving a proposal. -### Overview +| Contents | +| --------------------------------------------------- | +| [Proposal types](#proposal-types) | +| [Add a proposal](#add-proposal) | +| [View a proposal](#view-proposal) | +| [Get number of proposals](#get-number-of-proposals) | +| [Get proposals by status](#get-proposals-by-status) | +| [Vote on a proposal](#vote-on-a-proposal) | -| Action | Description | -| ---------------------------------------------------- | ------------------------------------------------ | -| [Proposal Kinds](#proposal-kinds) | Types of proposals you can create on the DAO. | -| **METHODS** | | -| [`add_proposal`](#add-proposal) | Adds proposal to the DAO. | -| [`view_proposal`](#view-proposal) | Views proposal by ID. | -| [`get_num_proposals`](#get-proposal-count) | Gets total count of active proposals on the DAO. | -| [`get_proposals_by_status`](#get-proposal-by-status) | Gets all proposals by status. | -| [`approve_proposal`](#approve-proposal) | Approves the proposal. | -| [`finalize`](#finalize) | | +--- -### Proposal Kinds +### Proposal types -- Each kind of proposal represents an operation the DAO can perform. Here are the proposal options: +- Each kind of proposal represents an operation the DAO can perform. Here are the kinds of proposals: ```rs ProposalKind::ChangeConfig { .. } => "config", @@ -277,6 +273,8 @@ ProposalKind::BountyDone { .. } => "bounty_done", ProposalKind::Vote => "vote", ``` +--- + ### Add proposal - By default, anyone can add a proposal. Adding a proposal is performed by calling `add_proposal` on your DAO contract. @@ -303,6 +301,8 @@ Example of ARG Structure: near call YOUR_SPUTNIK_DAO_CONTRACT add_proposal '{"proposal": {"description": "Add New Council", "kind": {"AddMemberToRole": {"member_id": "council_member_3.testnet", "role": "council"}}}}' --accountId proposer.testnet --amount 1 ``` +--- + ### View proposal - Anyone can view a proposal by calling `get_proposal` and passing the `id` of the proposal they wish to view: @@ -311,14 +311,20 @@ near call YOUR_SPUTNIK_DAO_CONTRACT add_proposal '{"proposal": {"description": " near view YOUR_DAO_ACCOUNT get_proposal '{"id": 0}' ``` -### Get proposal count +--- + +### Get number of proposals - Gets total number of proposals on DAO +--- + ### Get proposals by status - Gets proposals by status or multiple statuses depending on method used. +--- + ### Approve proposal - Only council members can approve a proposal. Approving a proposal is performed by calling `act_proposal` and passing an `id` and `action` with `VoteApprove` as the value. @@ -335,7 +341,7 @@ near call YOUR_DAO_ACCOUNT act_proposal '{"id": ID_from_previous_call, "action": > -### Voting on a proposal +### Vote on a proposal > Only council members are allowed to vote on a proposal. From 4ed28bde4aa974fdcb51733de3a45c332ca7cdb9 Mon Sep 17 00:00:00 2001 From: Josh Date: Tue, 29 Jun 2021 00:00:08 -0700 Subject: [PATCH 12/26] add proposal --- sputnikdao2/README.md | 59 ++++++++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/sputnikdao2/README.md b/sputnikdao2/README.md index f44699e7b..5d2183cd6 100644 --- a/sputnikdao2/README.md +++ b/sputnikdao2/README.md @@ -216,9 +216,9 @@ near view $SPUTNIK_ID get_policy ## Roles and Permissions -> The DAO can have several roles, each of which allows for permission configuring. These permissions are a combination of [`proposal_kind`](#proposal-types) and `VotingAction`. Due to this combination these permissions can be scoped to be very specific or you can use wildcards to allow roles to have greater access. +> The DAO can have several roles, each of which allows for permission configuring. These permissions are a combination of [`proposal_kind`](#proposal-types) and `VotingAction`. Due to this combination these permissions can be scoped to be very specific or you can use wildcards to grant greater access. -_Example:_ +**Examples:** - A role with: `["mint:VoteReject","mint:VoteRemove"]` means they can only vote to _reject_ or _remove_ a `mint` proposal but they can't vote to approve. @@ -226,7 +226,7 @@ _Example:_ - 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 all seven actions:** +**Here is a list of actions:** - `AddProposal` - _Adds proposal which is used internally._ - `RemoveProposal` - _Removes given proposal which is used for immediate deletion in special cases._ @@ -255,7 +255,7 @@ _Example:_ ### Proposal types -- Each kind of proposal represents an operation the DAO can perform. Here are the kinds of proposals: +> Each kind of proposal represents an operation the DAO can perform. Here are the kinds of proposals: ```rs ProposalKind::ChangeConfig { .. } => "config", @@ -277,9 +277,19 @@ ProposalKind::Vote => "vote", ### Add proposal -- By default, anyone can add a proposal. Adding a proposal is performed by calling `add_proposal` on your DAO contract. +> 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). -Example of ARG Structure: +- method: `add_proposal` +- params: + - `proposal` + - `description` + - `kind` +- proposer account ID +- attached deposit (minimum 1 Ⓝ) + +

+Example argument structure: +

```json { @@ -295,34 +305,36 @@ Example of ARG Structure: } ``` -**Example `near-cli` command:** +

+
+ +
+Example near-cli command: +

```bash -near call YOUR_SPUTNIK_DAO_CONTRACT add_proposal '{"proposal": {"description": "Add New Council", "kind": {"AddMemberToRole": {"member_id": "council_member_3.testnet", "role": "council"}}}}' --accountId proposer.testnet --amount 1 +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 ``` ---- - -### View proposal - -- Anyone can view a proposal by calling `get_proposal` and passing the `id` of the proposal they wish to view: +**RESPONSE:** ```bash -near view YOUR_DAO_ACCOUNT get_proposal '{"id": 0}' +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 ``` ---- - -### Get number of proposals +**Note:** The number under the transaction link is the proposal ID. -- Gets total number of proposals on DAO +

+
--- -### Get proposals by status - -- Gets proposals by status or multiple statuses depending on method used. - --- ### Approve proposal @@ -332,7 +344,8 @@ near view YOUR_DAO_ACCOUNT get_proposal '{"id": 0}' **Example:** ```bash -near call YOUR_DAO_ACCOUNT act_proposal '{"id": ID_from_previous_call, "action": "VoteApprove"}' --accountId council_member_1.testnet +near call genesis.sputnik-v2.testnet act_proposal '{"id": ID_from_previous_call, "action": "VoteApprove"}' \ +--accountId council_member_1.testnet ``` --- From e129240ac1c8f9384ed53bfce73c3b543b65587b Mon Sep 17 00:00:00 2001 From: Josh Date: Tue, 29 Jun 2021 00:00:46 -0700 Subject: [PATCH 13/26] add view proposal --- sputnikdao2/README.md | 45 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/sputnikdao2/README.md b/sputnikdao2/README.md index 5d2183cd6..c91021201 100644 --- a/sputnikdao2/README.md +++ b/sputnikdao2/README.md @@ -335,6 +335,51 @@ https://explorer.testnet.near.org/transactions/HbJdK9AnZrvjuuoys2z1PojdkyFiuWBvr --- +### View proposal + +> Returns proposal details by passing the ID or index of a given proposal. + +- method: `get_proposal` + - params: `id` + +
+Example near-cli command: +

+ +```bash +near view genesis.sputnik-v2.testnet get_proposal '{"id": 0}' +``` + +

+
+ +
+Example response: +

+ +```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" +} +``` + +

+
+ +--- + --- ### Approve proposal From a3fbfc9fd89747bd73925a24292ed7ea719f4f18 Mon Sep 17 00:00:00 2001 From: Josh Date: Tue, 29 Jun 2021 00:01:17 -0700 Subject: [PATCH 14/26] add view multiple proposals --- sputnikdao2/README.md | 58 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/sputnikdao2/README.md b/sputnikdao2/README.md index c91021201..bb5b88ce8 100644 --- a/sputnikdao2/README.md +++ b/sputnikdao2/README.md @@ -380,6 +380,64 @@ near view genesis.sputnik-v2.testnet get_proposal '{"id": 0}' --- +### View multiple proposals + +> Returns multiple proposal details by passing the index ("ID") starting point and a limit of how many records you would like returned. + +- method: `get_proposals` +- params: + - `from_index` + - `limit` + +
+Example near-cli command: +

+ +```bash +near view genesis.sputnik-v2.testnet get_proposals '{"from_index": 1, "limit": 2}' +``` + +

+
+ +
+Example response: +

+ +```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' + } +] +``` + +

+
+ +--- + --- ### Approve proposal From 5f492461241af34226c838e4371b243dbe18055f Mon Sep 17 00:00:00 2001 From: Josh Date: Tue, 29 Jun 2021 20:20:47 -0700 Subject: [PATCH 15/26] added approve proposal --- sputnikdao2/README.md | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/sputnikdao2/README.md b/sputnikdao2/README.md index bb5b88ce8..e5e7337d1 100644 --- a/sputnikdao2/README.md +++ b/sputnikdao2/README.md @@ -247,8 +247,7 @@ near view $SPUTNIK_ID get_policy | [Proposal types](#proposal-types) | | [Add a proposal](#add-proposal) | | [View a proposal](#view-proposal) | -| [Get number of proposals](#get-number-of-proposals) | -| [Get proposals by status](#get-proposals-by-status) | +| [View multiple proposals](#view-multiple-proposals) | | [Vote on a proposal](#vote-on-a-proposal) | --- @@ -416,7 +415,7 @@ near view genesis.sputnik-v2.testnet get_proposals '{"from_index": 1, "limit": 2 status: 'InProgress', vote_counts: {}, votes: {}, - submission_time: '1624947785010147691' + submission_time: '1624947785010147691' }, { id: 2, @@ -438,19 +437,25 @@ near view genesis.sputnik-v2.testnet get_proposals '{"from_index": 1, "limit": 2 --- ---- - ### Approve proposal -- Only council members can approve a proposal. Approving a proposal is performed by calling `act_proposal` and passing an `id` and `action` with `VoteApprove` as the value. +> Approves proposal by ID. Only council members can approve a proposal + +- method: `act_proposal` +- params: + - `id` + - `action` +- account ID that is a council member. **Example:** ```bash -near call genesis.sputnik-v2.testnet act_proposal '{"id": ID_from_previous_call, "action": "VoteApprove"}' \ +near call genesis.sputnik-v2.testnet act_proposal '{"id": 0, "action": "VoteApprove"}' \ --accountId council_member_1.testnet ``` +//TODO: add example response (testnet down) + --- ## Voting From 243aeb66b7b4c6dd53f51db9eca9668e2c42de4c Mon Sep 17 00:00:00 2001 From: Sherif Abushadi Date: Wed, 30 Jun 2021 20:56:14 +0300 Subject: [PATCH 16/26] minor fixes and add explainers to proposal types --- sputnikdao2/README.md | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/sputnikdao2/README.md b/sputnikdao2/README.md index e5e7337d1..5bbdb45cc 100644 --- a/sputnikdao2/README.md +++ b/sputnikdao2/README.md @@ -228,13 +228,13 @@ near view $SPUTNIK_ID get_policy **Here is a list of actions:** -- `AddProposal` - _Adds proposal which is used internally._ -- `RemoveProposal` - _Removes given proposal which is used for immediate deletion in special cases._ +- `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 (because it's spam)._ -- `Finalize` - _Finalizes proposal which is canalled when proposal has expired and returns funds._ -- `MoveToHub` - _Move a proposal to the hub to shift into another DAO._ +- `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)._ --- @@ -272,6 +272,20 @@ ProposalKind::BountyDone { .. } => "bounty_done", ProposalKind::Vote => "vote", ``` +- **ChangeConfig** - used to change the configuration of the DAO +- **ChangePolicy** - used to change the policy of the DAO +- **AddMemberToRole** - used to add a member to a role in the DAO +- **RemoveMemberFromRole** - used to remove a member from a role in the DAO +- **FunctionCall** - used to a call a function on any valid account on the network including the DAO itself, any other DAO, or any other contract. This is a useful mechanism for extending the capabilities of the DAO without modifying or complicating the DAO contract code. One can imagine a family of contracts built specifically to serve the DAO as agents, proxies, oracles and banks, for example. +- **UpgradeSelf** - used to upgrade the DAO contract itself. Consider using `FunctionCall` to extend the capabilities of the DAO before modifying DAO contract code directly. +- **UpgradeRemote** - used to upgrade other contracts. This capability pairs nicely with the `FunctionCall` proposal type. +- **Transfer** - used to move assets from this DAO to any other another account on the network. +- **Mint** - used to mint tokens controlled by the DAO +- **Burn** - used to burn tokens controlled by the DAO +- **AddBounty** - used to add a bounty to encourage members of the DAO community to contribute their time and attention to the needs of the DAO +- **BountyDone** - used to mark the completion of an available bounty +- **Vote** - used to vote on existing proposals + --- ### Add proposal @@ -384,7 +398,7 @@ near view genesis.sputnik-v2.testnet get_proposal '{"id": 0}' > Returns multiple proposal details by passing the index ("ID") starting point and a limit of how many records you would like returned. - method: `get_proposals` -- params: +- params: - `from_index` - `limit` From 3a39ea7ed5ce58a1af0c3072fc0874bde9aec720 Mon Sep 17 00:00:00 2001 From: Constantin Dogaru Date: Thu, 6 Jan 2022 13:42:29 +0200 Subject: [PATCH 17/26] address some review comments --- sputnikdao2/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sputnikdao2/README.md b/sputnikdao2/README.md index df586e53d..cd64f01a1 100644 --- a/sputnikdao2/README.md +++ b/sputnikdao2/README.md @@ -278,11 +278,11 @@ ProposalKind::Vote, - **FunctionCall** - used to a call a function on any valid account on the network including the DAO itself, any other DAO, or any other contract. This is a useful mechanism for extending the capabilities of the DAO without modifying or complicating the DAO contract code. One can imagine a family of contracts built specifically to serve the DAO as agents, proxies, oracles and banks, for example. - **UpgradeSelf** - used to upgrade the DAO contract itself. Consider using `FunctionCall` to extend the capabilities of the DAO before modifying DAO contract code directly. - **UpgradeRemote** - used to upgrade other contracts. This capability pairs nicely with the `FunctionCall` proposal type. -- **Transfer** - used to move assets from this DAO to any other another account on the network. +- **Transfer** - used to move assets from this DAO to another account on the network. Supports both `NEAR` and any `NEP-141` token that this DAO has. - **SetStakingContract** - used to set the staking contract of the DAO to help users delegate their tokens. - **AddBounty** - used to add a bounty to encourage members of the DAO community to contribute their time and attention to the needs of the DAO - **BountyDone** - used to mark the completion of an available bounty -- **Vote** - used to vote on existing proposals +- **Vote** - used to create polls. Vote proposal doesn't have any action. --- @@ -496,10 +496,10 @@ When vote policy is `RoleWeight(role)`, vote % is measured against the count of > DAO votes to select some token to become voting token (only can be done once, can't change later). -User flow is next: +User flow to vote with selected token: -- User's deposit the token into the DAO. -- They can then choose who to delegate these tokens. It can be to themself or to other users to increase their vote weight. +- Users deposit the desired amount of the token to the separate staking contract defined by the DAO. +- They can then choose who to delegate these tokens. It can be to themselves or to other users to increase their vote weight. - When users vote for proposals, their vote is weighted by all the delegations to them. - Undelegating will block delegating / withdrawing until one voting period passes. - Undelegated tokens can be withdrawn by the user. From eb12ed4ab185a72c89bb3389e8aab58c07535346 Mon Sep 17 00:00:00 2001 From: Constantin Dogaru Date: Thu, 6 Jan 2022 14:35:21 +0200 Subject: [PATCH 18/26] replace todo with example response --- sputnikdao2/README.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/sputnikdao2/README.md b/sputnikdao2/README.md index cd64f01a1..a304579bd 100644 --- a/sputnikdao2/README.md +++ b/sputnikdao2/README.md @@ -459,14 +459,23 @@ near view genesis.sputnik-v2.testnet get_proposals '{"from_index": 1, "limit": 2 - `action` - account ID that is a council member. -**Example:** +**Example near-cli command:** ```bash near call genesis.sputnik-v2.testnet act_proposal '{"id": 0, "action": "VoteApprove"}' \ --accountId council_member_1.testnet ``` -//TODO: add example response (testnet down) +**RESPONSE:** + +```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 +'' +``` --- From 774b6424e2d7e0a8bba74bc6698162241f3b0ce2 Mon Sep 17 00:00:00 2001 From: Constantin Dogaru Date: Thu, 6 Jan 2022 14:52:37 +0200 Subject: [PATCH 19/26] remove upgrade example from the end --- sputnikdao2/README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/sputnikdao2/README.md b/sputnikdao2/README.md index a304579bd..ffd1824ec 100644 --- a/sputnikdao2/README.md +++ b/sputnikdao2/README.md @@ -543,9 +543,3 @@ Blob lifecycle: - Remove blob and receive funds locked for storage back. Blob can be removed only by the original storer. - ---- - -``` - -``` From c75cfb4624b750be277fca285c6117078df930db Mon Sep 17 00:00:00 2001 From: Constantin Dogaru Date: Thu, 6 Jan 2022 14:57:53 +0200 Subject: [PATCH 20/26] address FunctionCall review comments --- sputnikdao2/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sputnikdao2/README.md b/sputnikdao2/README.md index ffd1824ec..9e191b384 100644 --- a/sputnikdao2/README.md +++ b/sputnikdao2/README.md @@ -276,8 +276,8 @@ ProposalKind::Vote, - **AddMemberToRole** - used to add a member to a role in the DAO - **RemoveMemberFromRole** - used to remove a member from a role in the DAO - **FunctionCall** - used to a call a function on any valid account on the network including the DAO itself, any other DAO, or any other contract. This is a useful mechanism for extending the capabilities of the DAO without modifying or complicating the DAO contract code. One can imagine a family of contracts built specifically to serve the DAO as agents, proxies, oracles and banks, for example. -- **UpgradeSelf** - used to upgrade the DAO contract itself. Consider using `FunctionCall` to extend the capabilities of the DAO before modifying DAO contract code directly. -- **UpgradeRemote** - used to upgrade other contracts. This capability pairs nicely with the `FunctionCall` proposal type. +- **UpgradeSelf** - used to upgrade the DAO contract itself. +- **UpgradeRemote** - used to upgrade other contracts. For DAOs that are governing other protocols, this type of proposal will allow to upgrade another contract with its newer version. - **Transfer** - used to move assets from this DAO to another account on the network. Supports both `NEAR` and any `NEP-141` token that this DAO has. - **SetStakingContract** - used to set the staking contract of the DAO to help users delegate their tokens. - **AddBounty** - used to add a bounty to encourage members of the DAO community to contribute their time and attention to the needs of the DAO From 47cf845770d54254a20e5412bced0a0b8cdb476b Mon Sep 17 00:00:00 2001 From: Constantin Dogaru Date: Thu, 6 Jan 2022 15:35:13 +0200 Subject: [PATCH 21/26] remove non-working examples section --- sputnikdao2/README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/sputnikdao2/README.md b/sputnikdao2/README.md index 9e191b384..fe7c2b24f 100644 --- a/sputnikdao2/README.md +++ b/sputnikdao2/README.md @@ -12,7 +12,6 @@ | [Voting](#voting) | Configure policies, setup governance tokens, and vote on proposals. | | [Bounties](#bounties) | Add and configure bounties. | | [Blob Storage](#blob-storage) | Store large data blobs and content and index them by the data's hash. | -| [Examples](./examples/README.md) | Examples and guides for interacting with Sputnik DAO v2. | --- From a0177dcda1cf91b9cf8940f38e9eb96e3e2af2b1 Mon Sep 17 00:00:00 2001 From: Constantin Dogaru Date: Thu, 6 Jan 2022 15:41:50 +0200 Subject: [PATCH 22/26] replace non existent mint with transfer --- sputnikdao2/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sputnikdao2/README.md b/sputnikdao2/README.md index fe7c2b24f..d98abf9db 100644 --- a/sputnikdao2/README.md +++ b/sputnikdao2/README.md @@ -219,9 +219,9 @@ near view $SPUTNIK_ID get_policy **Examples:** -- A role with: `["mint:VoteReject","mint:VoteRemove"]` means they can only vote to _reject_ or _remove_ a `mint` proposal but they can't vote to approve. +- 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: `["mint:*"]` can perform any vote action on `mint` proposals. +- 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. From 3d25c4000483ab07fa67a4a0d78a27792d071283 Mon Sep 17 00:00:00 2001 From: Constantin Dogaru Date: Thu, 6 Jan 2022 16:19:49 +0200 Subject: [PATCH 23/26] fix proposal examples indentation --- sputnikdao2/README.md | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/sputnikdao2/README.md b/sputnikdao2/README.md index d98abf9db..73aadafae 100644 --- a/sputnikdao2/README.md +++ b/sputnikdao2/README.md @@ -329,7 +329,12 @@ near call genesis.sputnik-v2.testnet add_proposal \ --amount 1 ``` -**RESPONSE:** +

+
+ +
+Example response: +

```bash Transaction Id HbJdK9AnZrvjuuoys2z1PojdkyFiuWBvrDbXsAf5ndvu @@ -458,14 +463,21 @@ near view genesis.sputnik-v2.testnet get_proposals '{"from_index": 1, "limit": 2 - `action` - account ID that is a council member. -**Example near-cli command:** +

+Example near-cli command: +

```bash near call genesis.sputnik-v2.testnet act_proposal '{"id": 0, "action": "VoteApprove"}' \ --accountId council_member_1.testnet ``` -**RESPONSE:** +

+
+ +
+Example response: +

```bash Receipts: 3mkSgRaHsd46FHkf9AtTcPbNXkYkxMCzPfJFHsHk8NPm, GjJ6hmoAhxt2a7si4hVPYZiL9CWeM5fmSEzMTpC7URxV @@ -476,6 +488,9 @@ https://explorer.testnet.near.org/transactions/BZPHxNoBpyMG4seCojzeNrKpr685vWPyn '' ``` +

+
+ --- ## Voting From 230cc306c6e993ab3eff2b64d092f675ee45d24d Mon Sep 17 00:00:00 2001 From: Constantin Dogaru Date: Wed, 12 Jan 2022 14:54:00 +0200 Subject: [PATCH 24/26] small fix --- sputnikdao2/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sputnikdao2/README.md b/sputnikdao2/README.md index c17a8f9f2..971339310 100644 --- a/sputnikdao2/README.md +++ b/sputnikdao2/README.md @@ -569,6 +569,6 @@ Blob can be removed only by the original storer. There are two major ways to upgrade the DAO: - Self upgrade by storing blob on the DAO contract and then voting to UpgradeSelf - - Upgrade from the factory - factory stores new contract and then if allowed upgrades DAO by calling `upgrade(code)`. + - Upgrade from the factory - factory stores new contract and then, if allowed, it upgrades the DAO by calling `upgrade(code)`. -DAO contracts can explicitly vote to disable factory auto upgrades and require to pull the upgrade themself from factory. +DAOs can explicitly vote to disable factory auto upgrades and can pull the upgrade themselves from the factory. From 14611f95c644719ea8ba9282b0c05f275677e71b Mon Sep 17 00:00:00 2001 From: Constantin Dogaru Date: Wed, 12 Jan 2022 15:03:33 +0200 Subject: [PATCH 25/26] solve table of contents in the proposal section --- sputnikdao2/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sputnikdao2/README.md b/sputnikdao2/README.md index 971339310..05731a0af 100644 --- a/sputnikdao2/README.md +++ b/sputnikdao2/README.md @@ -245,10 +245,10 @@ near view $SPUTNIK_ID get_policy | Contents | | --------------------------------------------------- | | [Proposal types](#proposal-types) | -| [Add a proposal](#add-proposal) | -| [View a proposal](#view-proposal) | +| [Add proposal](#add-proposal) | +| [View proposal](#view-proposal) | | [View multiple proposals](#view-multiple-proposals) | -| [Vote on a proposal](#vote-on-a-proposal) | +| [Approve proposal](#approve-proposal) | --- From f0bc895cf747a8fb9f05a17f9d1ec1dcb0df2b1d Mon Sep 17 00:00:00 2001 From: Constantin Dogaru Date: Wed, 12 Jan 2022 15:10:28 +0200 Subject: [PATCH 26/26] order sections in the right way --- sputnikdao2/README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sputnikdao2/README.md b/sputnikdao2/README.md index 05731a0af..47004ca5f 100644 --- a/sputnikdao2/README.md +++ b/sputnikdao2/README.md @@ -16,7 +16,9 @@ --- -## Prerequisites +## Setup + +### Prerequisites 1. [NEAR Account](https://wallet.testnet.near.org) 2. [NEAR-CLI](https://docs.near.org/docs/tools/near-cli#setup) @@ -51,8 +53,6 @@ rustup target add wasm32-unknown-unknown --- -## Setup -
1. Login with your account.

@@ -504,6 +504,8 @@ https://explorer.testnet.near.org/transactions/BZPHxNoBpyMG4seCojzeNrKpr685vWPyn > Only council members are allowed to vote on a proposal. +--- + ### Voting policy > You can set a different vote policy for each one of the proposal kinds.