---
# System prepended metadata

title: Dao Database Spec
tags: [v3]

---

---
tags: v3
---

# Dao Database Spec

## What
Outline specs for a flexible DAO database using the poster contract and subgraphs. This will initially used for DAO Metadata, but expandable to any domain/entity.


### Posting

- Update all locations in the app that write metadata
    - dao data
    - custom theme
    - boost activation/playlists
- Now a transaction to the poster contract
    - tag specific to the target table/entity
    - content in json
- member only access to the forms, validated in the subgraph
- tag in dot notation indicating daohaus/validation type/target table
    - v2 will only have member validation
        - minion validation is in place and used in the docs boost
    - v3 will add baal validation


### Entities and Schema

Create a single Record entity in the subgraph
- each post adds a new entry
- ui can query by 'table' field and sort for the newest to get the latest snapshot of metadata for that table type
    - explore a single entity updated on each post in additon to the full transaction history for easier lookup/query


Entity

```gql

type Posts @entity {
  id: ID!
  createdAt: String!
  createdBy: Bytes!
  dao: Dao!
  tag: Bytes!
  table: String!
  contentType: String!
  content: String!
}
```


1. general metadata

tag: daohaus.member.metadata
table: 'metadata'
contentType: json
content:

```json=
{
    "dao": "0x0",
    "name": "kovan cco v2",
    "description": "tester",
    "longDescription": "some long description",
    "purpose": "Products",
    "avatarImg": "QmWLh8vCksEhmPQsyS6BgXtytkBAmtoJ68qpiTuJx47u22"
    "links": {
        "website": "",
        "twitter": "",
        "discord": "https://discord.gg/V4M4h5EX",
        "telegram": "",
        "medium": "",
        "other": ""
    },
    "tags": ['some', 'tags'],
}

```

2. boosts

tag: daohaus.member.boost
table: 'boost'
contentType: json
content example:
```json=
"boosts": {
    "dao": "0x0",
    "boost-name": {
    "active": true,
        "metadata": {
            "some-data-point": "10000",
            "another": "1",
        },
    }
}
```

3. proposalConfig

tag: daohaus.member.proposalConfig
table: 'proposalConfig'
contentType: json
content:

```json=
"proposalConfig": {
    "dao": "0x0",
    "playlists": [
        {
            "name": "Favorites",
            "id": "favorites",
            "forms": [
                "BUY_SHARES",
                "SHARES_FOR_WORK",
                "TOKEN",
                "GUILDKICK"
            ]
        },
        {
            "name": "The Classics",
            "id": "classics",
            "forms": [
                "MEMBER",
                "FUNDING",
                "TOKEN",
                "TRADE",
                "GUILDKICK",
                "SIGNAL"
            ]
        }
    ]
}
```
***TODO: is the allForms element in proposal configs still used in the front end?***
https://data.daohaus.club/dao/0x1dea6975c6263ac6ca6ee635085682d2ea31dfee


4. customTheme

tag: daohaus.member.customTheme
table: 'customTheme'
contentType: json
content:

```json=
"customThemeConfig": {
    "dao": "0x0",
    "primary500": "#6b1e63",
    "primaryAlpha": "rgba(107,30,99,0.9)",
    "secondary500": "#0fe4f3",
    "secondaryAlpha": "rgba(15,228,243,0.75)",
    "bg500": "#12031b",
    "bgAlpha": "#03061B",
    "bgOverlayOpacity": 0.75,
    "modeAlpha500": "#FFFFFF",
    "headingFont": "Bungee",
    "bodyFont": "Space Grotesk",
    "monoFont": "JetBrains Mono",
    "avatarImg": "/static/media/Daohaus__Castle--Dark.3084fa93.svg",
    "bgImg": "QmTWgNDYCirnS4os8eip5bXDfv6SxU2EFatkPvEABADHyv",
    "daoMeta": {
        "proposals": "Proposals",
        "proposal": "Proposal",
        "bank": "Bank",
        "members": "Members",
        "member": "Member",
        "boosts": "Apps",
        "boost": "App",
        "settings": "Settings",
        "ragequit": "Rage Quit",
        "guildKick": "Guild Kick",
        "minion": "Minion",
        "minions": "Minions"
    }
},


```



## To do
- Poster deployment on all networks
    - audit to see where we need to deploy

- Data backfilling strategy
    - might be too many transactions back fill locally



## v3

### Overview

Specification for a riduclously simple DAO managed database utilizing Poster contracts and the daohaus v3 subgraph. 

### Features

**1. Enable DAOs to own metadata**
- stored in contract events and the daohaus subgraph and esily queried with other dao data
- only updated by DAO members
    - validations levels: loot holder, share holder, ratified by proposal

**2. Flexible and extendbale db tables and fields**
- this spec will define the schema for basic DAO profile metadata

**3. Store a history of data updates**

### Specifcation

#### Writing data

Writing DAO data will be done through the post function in the poster contract on that DAOs network. Poster has been deployed at `0x000000000000cd17345801aa8147b8D3950260FF` on most networks.


```solidity
contract Poster {

    event NewPost(address indexed user, string content, string indexed tag);

    function post(string calldata content, string calldata tag) public {
        emit NewPost(msg.sender, content, tag);
    }
}
```

#### post function arguments

##### string calldata content
Json string with relevant key/values for the type of record.

DAO profile record:
```json=
{
    "dao": "0x0",
    "name": "kovan cco v2",
    "description": "tester",
    "longDescription": "some long description",
    "avatarImg": "QmWLh8vCksEhmPQsyS6BgXtytkBAmtoJ68qpiTuJx47u22"
    "links": {
        "website": "",
        "twitter": "",
        "discord": "https://discord.gg/V4M4h5EX",
        "telegram": "",
        "medium": "",
        "other": ""
    },
    "tags": ['some', 'tags'],
}


TODO: test why the json was breaking
{
    "dao": "0x0",
    "data": "encodeddata",
}
- also look into poster subgraph
- (peng) remove the trailing comma after "encodedata" and there should be one after avatarImg
//why did we encode - json parser broke on certain characters
// 
```
- 'dao' is a required field on all posts and will be used for lookup in the subgraph mappings
- all fields will be intrpreted as strings unless indicated
- avatarImg should be a ipfs hash
- links will be a json string of link key/value pairs

**TODO: discuss including content type in fields**
- types could include string, ipfs, markdown, nested json
- downside is it adds more overhead
```json=
{
    "name": {
        content: '0x0',
        contentType: 'string'
    },
    "avatarImg": {
        content: "QmWLh8vCksEhmPQsyS6BgXtytkBAmtoJ68qpiTuJx47u22",
        contentType: 'ipfs'
    },
      "avatarImg": {
        content: "adsdsdsdWLh8vCksasdsdsdsdsdsd",
        contentType: 'arweave'
    },
    "bgImg": {
        content: "http://something.else",
        contentType: 'url'
    }
    "longDescription: {
    content: 'aokjfsdfksdfksdflk',
    contentType: 'encoded base64'
}
}
}
```

##### string calldata tag
String with dot notation that will aid subgraph indexing and parsing. These are emited as indexed params so they are not human readible in events and subgraph mappings.

Format: `<app: daohaus>.<pemission level>.<target table>`

Permission levels:
```
- member: share or loot holder
- shares: share holder
- loot: loot holder
- proposal: post must be done in the execution action of a passed proposal 
```

We will only have one target table at alpha stage used for DAO profile.

DAO profile record tag: `daohaus.shares.daoProfile`


#### Indexing data

The daohaus v3 subgraph will subscribe the the `NewPost` event and map valid data into Record entities.



```gql
type Record @entity {
  id: ID!
  createdAt: String!
  createdBy: Bytes!
  dao: Dao!
  tag: Bytes!
  table: String!
  contentType: String!
  content: String!
  // title
  // description
  // add search field
}
```
- 
- content type will always be json for these alpha records, but adding for future flexibility


##### validation
- only defined tags will be indexed
- permission level validationperformed in the mapping based on permission level in tag
    - mapping will lookup share/loot holders or ensure the msg.sender is the daos safe address
- validate content string is json and has a dao property with a valid dao address

##### mapping
- ID: block timestamp + log index
- createdAt: block timestamp
- createdBy: msg.sender (event.params.address)
- dao: dao address (parsed from json and validated)
- tag: event.params.tag
- table: String (derived form tag)
- contentType:will always be json for these alpha records, but adding for future flexibility)
- content: json from post - event.params.content


### Reading data
Record entities can be looked up by dao address + table. Sorting by the createdAt field, descending will return the most recent snapshot of the table's data.

example:

```.gql
{
    records(
        where: {dao: '0x0', table: 'daoProfile'}
        first: 1
        orderBy: createdAt
        orderDirection: desc
      ) 
     {id, content, table}
 }

```

**TODO: discuss adding a entity per table type to store the most recent data snapshot in additon to the historical record history**
- ie) single DaoProfile entity updated each time a new record comes in
- might eliminate confusion for clients not aware of how to query for the latest data

### Reference

[Poster Constract Spec](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-3722.md)

[Notes and v2 thoughts](https://hackmd.io/Z9X2yRlhSbaurnP5FVA-LQ?both)