NEAR-DevHub / neardevhub-treasury-dashboard

MIT License
1 stars 0 forks source link

Track configuration of the SputnikDAO contract to support the treasury workflow #15

Open Tguntenaar opened 5 months ago

Tguntenaar commented 5 months ago

This issue is to track/discuss changes that need to be made in order to support the treasury dashboard.

Research issue NEAR-DevHub/neardevhub-bos#622 is a relevent source for more information.

Our goal is to deploy a DAO on the treasury.devhub.near account with the right policy configuration.

The sputnik dao repo contains a dao factory to deploy daos as subaccounts. devhub.near however will not contain a factory but the smart contract to support devhub posts/moderators/communities etc..

We won't need a factory contract to manage multiple DAO's so we can just deploy a single DAO instance on the treasury.devhub.near account. On initialisation we can set the right policy of the DAO, but this can also be changed later on by submitting proposals of kind ChangePolicy.

In order to be able to edit the policy later on (editing trustees and moderators) there also needs to be a council role. Below I wrapped the policy config in the parameters of the ChangePolicy proposal to show case how that would work.

Change Policy parameters ```json { "proposal": { "kind": { "ChangePolicy": { "policy": { "roles": [{ "name": "council", "kind": { "Group": [ "devhub.near" ] }, "permissions": [ "*:Finalize", "*:AddProposal", "*:VoteApprove", "*:VoteReject", "*:VoteRemove" ], "vote_policy": { "Group": { "weight_kind": "RoleWeight", "quorum": "0", "threshold": 1 } } }, { "name": "trustees", "kind": { "Group": [ "thomasguntenaar.testnet", "efiz.testnet", "megha2001.testnet" ] }, "permissions": [ "call:Finalize", "call:VoteApprove", "call:VoteReject", "call:VoteRemove" ], "vote_policy": { "Group": { "weight_kind": "RoleWeight", "quorum": "0", "threshold": 1 } } }, { "name": "moderators", "kind": { "Group": [ "thomasguntenaar.testnet", "efiz.testnet", "megha2001.testnet" ] }, "permissions": [ "call:AddProposal", "call:RemoveProposal" ], "vote_policy": { "Group": { "weight_kind": "RoleWeight", "quorum": "0", "threshold": 1 } } } ], "default_vote_policy": { "weight_kind": "RoleWeight", "quorum": "0", "threshold": 1 }, "proposal_bond": "1000000000000000000000000", "proposal_period": "604800000000000", "bounty_bond": "1000000000000000000000000", "bounty_forgiveness_period": "86400000000000" } } }, "description": "This is a proposal to edit the permissions of the trustees and moderators role " } } ```

Acceptance Criteria

This is possible in 2 ways:

  1. A Transfer proposal
    1. Which takes token_id, receiver_id, amount and msg.
  2. A FunctionCall proposal where we call ft_transfer Which takes amount and recipient_id as base64 args
    1. It can reference the sponsorship post in the proposals description.
    2. We could add another field to the proposal struct what would contain a href string.

The address book of receivers should be stored in devhub.near contract. This way we can manage it from there without having to change/audit the DAO contract.

  1. Only moderators can add proposals
  2. We can make sure that moderators would only be able to select / view recipients that are both KYC verified and have received a test tx. (rendering auditing unnecessary)

Policy configuration in detail

Roles Permissions are strings formatted like this `[ProposalKind:Action]`. There are 3 roles: 1. council 4. moderators 5. trustees Council is soly there to be able to add/remove accounts from the other roles. Moderators can only _create_ or _remove_ payment requests by adding/removing a proposal of kind `FunctionCall` aka `call`. ```json { "name": "moderators", "permissions": [ "call:AddProposal", "call:RemoveProposal" ] } ``` Trustees can only vote to approve, reject, remove or finilize proposals. ```json { "name": "trustees", "permissions": [ "call:Finalize", "call:VoteApprove", "call:VoteReject", "call:VoteRemove" ] } ```
Default vote policy, Bonds & Bounty `default_vote_policy` is a fallback mechanism in case the role based vote policy is configured incorrectly. `proposal_bond` is what it the deposit cost is of a proposal. We don't need to set it high since only moderators are able to make proposals. `proposal_period` is the time limit on proposals. `bounty_bond` we won't use bounties. `bounty_forgiveness_period` won't use this either

Time estimation

The DAO contract is good to go and won't need additional edit, so no auditing needed.

What's left todo is:

  1. Find out how to deploy the DAO without a factory (1 hr)
  2. Find out who will be what role. (DAO policy makers, moderator and trustee). (1 hr)
  3. I would need just a few minutes to finalize the config after discussions.

The recipients address book will need:

  1. A user interface to manage it.
  2. The devhub.near will need a vector of recipient, an update function and a get_valid_recipients for the create payment request form. (1/2 day)

The indexer:

### Tasks
Tguntenaar commented 5 months ago

@ailisp @frol could you guys look at the change policy parameters section. I need to know who will be council, moderator and trustee before I can deploy the DAO. Most important is the council, since they can change moderators and trustees.

Tguntenaar commented 5 months ago

I'm re-posting the configuration by itself so we can discuss and edit it.

Some discussion points:

  1. Which accounts will have what role.
    1. The council will have a lot of power so we might not even want a council.
    2. If we do want a council we might change their voting policy to have a least a ratio of members to approve or a minimum of a least x amount of councilers.
  2. The proposal_bond is is primarily used to block spam but since only moderators will be able to call:AddProposal and they won't spam we could lower that bond by a lot.
  3. The proposal period

When deploying to treasury.devhub.near the init-function new will be called with the following arguments

pub fn new(config: Config, policy: VersionedPolicy) -> Self {

The config & policy will look like this:

{
  "config": {
    "name": "Devhub Trust DAO",
    "purpose": "Trustees vote on payment requests made by the moderators.",
    "metadata": ""
  },
  "policy": {
    "roles": [{
        "name": "council",
        "kind": {
          "Group": [
            "devhub.near",
            "thomasguntenaar.near",
            "frol.near",
            "bo.near"
          ]
        },
        "permissions": [
          "*:Finalize",
          "*:AddProposal",
          "*:VoteApprove",
          "*:VoteReject",
          "*:VoteRemove"
        ],
        "vote_policy": {
          "Group": {
            "weight_kind": "RoleWeight",
            "quorum": "0",
            "threshold": "1"
          }
        }
      },
      {
        "name": "trustees",
        "kind": {
          "Group": [
            "thomasguntenaar.near",
            "megha19.near",
            "petersalomonsen.near",
            "theori.near",
            "frol.near",
            "bo.near",
            "polyprogrammist.near"
          ]
        },
        "permissions": [
          "transfer:Finalize",
          "transfer:VoteApprove",
          "transfer:VoteReject",
          "transfer:VoteRemove",
          "call:Finalize",
          "call:VoteApprove",
          "call:VoteReject",
          "call:VoteRemove"
        ],
        "vote_policy": {
          "Group": {
            "weight_kind": "RoleWeight",
            "quorum": "0",
            "threshold": "1"
          }
        }
      },
      {
        "name": "moderators",
        "kind": {
          "Group": [
            "petersalomonsen.near",
            "thomasguntenaar.near",
            "theori.near",
            "megha19.near",
            "bo.near",
            "frol.near",
            "polyprogrammist.near"
          ]
        },
        "permissions": [
          "transfer:AddProposal",
          "transfer:RemoveProposal",
          "call:AddProposal",
          "call:RemoveProposal"
        ],
        "vote_policy": {
          "Group": {
            "weight_kind": "RoleWeight",
            "quorum": "0",
            "threshold": "1"
          }
        }
      }
    ],
    "default_vote_policy": {
      "weight_kind": "RoleWeight",
      "quorum": "0",
      "threshold": "1"
    },
    "proposal_bond": "1000000000000000000000000",
    "proposal_period": "604800000000000",
    "bounty_bond": "1000000000000000000000000",
    "bounty_forgiveness_period": "86400000000000"
  }
}
Tguntenaar commented 5 months ago

While calling the init function I get a very annoying error that tells me the policy is in the wrong format but not what is wrong. I have tested with numbers instead of strings but it won't play nice. So I will deploy it with a default policy than create an UpdatePolicy proposal and vote to approve it.

ExecutionError("Smart contract panicked: panicked at 'Failed to deserialize input from JSON.: Error(\"data did not match any variant of untagged enum VersionedPolicy\", line: 96, column: 1)', sputnikdao2/src/lib.rs:83:1")
Tguntenaar commented 5 months ago

Okay.. I found out that the threshold should be a string not a number. Apparently I didn't change all 4 thresholds correctly the last time I tested that.

frol commented 5 months ago

@Tguntenaar Thank you for preparing this extensive yet compact documentation!

Regarding Indexer for history of transactions: I don't think we need one, we should be able to use NearBlocks API

Regarding payment requests:

Regarding KYC/verification: I agree that it can be enforced on the process and UI level since we don't expect malicious moderators, and that will allow us to cut a corner there by avoiding modifications to the DAO contract.

Regarding the council role: We need to configure it in such a way that we need N-1 or N-2 votes in order to approve the proposal (I hope we won't need to exercise it, but if we need it, we should have this way out of whatever situation we arrive there, and we will need review from almost everyone - let me sync with Ori on it, but it is not a blocker for now)

Regarding proposal_period: I think we should make it 30 days instead of 7 days now. There is no real deadline, we will try to execute them as fast as possible, but if there is a week of holidays, we don't want to end up re-creating the proposals for no reason.

Overall, the config looks good to me. The quorums/thresholds will need to be adjusted when we will collect the whole list of moderators, councils, and trustees. Let's get it running on some test account on mainnet to have an end-to-end demo with some real accounts.

Tguntenaar commented 5 months ago

Moved it back because the discussions issue NEAR-DevHub/neardevhub-bos#584 has priority

Tguntenaar commented 4 months ago

@frol @Megha-Dev-19 The mainnet DAO is deployed to treasurydevhub.near.

To view the policy you can run:

near contract call-function as-read-only treasurydevhub.near get_policy json-args {} network-config mainnet now
Tguntenaar commented 4 months ago

The same policy is also deployed on treasurydevhub.testnet. Check out the policy by executing:

near contract call-function as-read-only treasurydevhub.testnet get_policy json-args {} network-config testnet now
Tguntenaar commented 4 months ago

This is an example on how to update the policy on testnet

If you want to do it on mainnet change the contract account and network-config as well as the signer.

Steps to update the policy if you are in the council:

  1. Get the current policy
near contract call-function as-read-only treasurydevhub.testnet get_policy json-args {} network-config testnet now
  1. Create a JSON file with this structure:
{
  "proposal": {
    "description": "Add frol's accounts+30day period",
    "kind": {
      "ChangePolicy": {
         "policy": <NEW POLICY HERE>
      }
    }
  }
}
  1. Add the proposal to update the policy by running this: (don't forget to replace the signer)
near contract call-function as-transaction treasurydevhub.testnet add_proposal file-args ./devhub-update-policy-testnet.json prepaid-gas '100.0 Tgas' attached-deposit '1 NEAR' sign-as REPLACE.testnet network-config testnet sign-with-keychain send

Executing this will return the newly created proposal ID which you will need for the next step.

  1. Act to approve the proposal. Depending on the policy this might take multiple votes from the council. (don't forget to replace the signer and proposal id.
near contract call-function as-transaction treasurydevhub.testnet act_proposal json-args '{"id": REPLACE_WITH_ID,"action": "VoteApprove","memo": ""}' prepaid-gas '300.0 Tgas' attached-deposit '0 NEAR' sign-as REPLACE.testnet network-config testnet sign-with-keychain send
Tguntenaar commented 4 months ago

Moving it back again to work on the indexer of the proposals