Skip to content

Commit

Permalink
⚡️ v0.1
Browse files Browse the repository at this point in the history
- Slashing mechanism
- Epoch snapshot in a document-oriented DB
- Reports are now independent from epoch snapshotting (and can be extended by working on `reporter/reporter.py`)
  • Loading branch information
dantop114 authored Jan 28, 2022
2 parents 3104d0a + 5b5c827 commit 272486b
Show file tree
Hide file tree
Showing 75 changed files with 25,177 additions and 12,069 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ __pycache__
.hypothesis/
build/
.vscode/
node_modules/
node_modules/
env/
56 changes: 24 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,52 +2,44 @@

A collection of scripts enabling analysis of PieDAO staking initiative.

## Folder structure overview

```
pie-reporter: a collection of scripts enabling analysis of PieDAO staking initiative
├── interfaces: helpful interfaces to interact with pie-dao contracts via brownie
├── reports: contains reports (atm only for the staking initiative)
│ ├── airdrops: airdrops reports (merkle tree, amounts...)
│ ├── epochs: staking rewards epochs (merkle tree, amounts, )
│ └── staking: staking reports (csv files, useful for data analysis & extraction)
└── scripts: the actual scripts
├── check-slice-redeem.py: script used to simulate the claim for all the stakers
├── CreateClaims.ts: used to generate merkle tree and claims
├── edough-compounding.py: used to generate and post transactions
├── helpers.py: utility methods (mostly json/csv io)
└── reporter.py: the reporter, generates all the staking reports and needed data
```

## How to run

First, install `brownie-eth`:
First, init a virtualenv:

```
python3.7 -m venv ~/brownie_venv # creates a python virtualenv
source ~/brownie_venv/bin/activate
pip install eth-brownie (see https://eth-brownie.readthedocs.io/en/stable/install.html)
python3.8 -m venv ./env # creates a python virtualenv
source ./env/activate
pip install -r requirements.txt
```

To run the `reporter`:
To build a distribution:

```
brownie run reporter report
python ./reporter/run.py conf
python ./reporter/run.py build <current-epoch-folder> <previous-epoch-folder>
python ./reporter/run.py report <current-epoch-folder>
```

## Reports generated explained for Dummies

```
reports/staking/<month-year>
├── proposals.csv: All eligible proposals considered for the reporting month
├── slice_amounts.csv: Slice amounts entitled for each address for the current month's treasury distribution depending on their voting weight
├── slice_amounts_after_unclaimed.csv: every eligible and non eligible addresses and their accrued rewards. Use for the notarization but delete the not eligible addressess to leave only those eligible during the reporting month.
├── stakers.csv: Amount of veDough staked per address
├── unclaimed.csv: amount of unclaimed rewards per address
├── voted.csv: addresses that voted on the reporting month
└── not_voted.csv: addresses that did not vote on the reporting month
reports/<month-year>
├── csv
├── distribution.csv: Rewards distributed for this month (including slashed)
├── rewards.csv: Rewards distributed including any unclaimed reward
├── voters.csv: Addresses that voted on the reporting month
├── non_voters.csv: Addresses that did not vote on the reporting month
├── proposals.csv: All eligible proposals considered for the reporting month
└── stakers.csv: Amount of veDough staked per address
├── json
├── rewards.json: rewards.csv in a JSON format
└── stakers.json: stakers.csv in a JSON format
├── claims.json: Claims for this month (used to generate the merkle tree)
├── epoch-conf.json: Epoch configuration
├── reporter-db.json: JSON format document-based database (used by the reporter)
└── merkle-tree.json: Final merkle tree
```

### Future improvements

- [ ] Implement a sqlite database for reports
- [ ] Implement a CLI to query `reporter-db`
77 changes: 0 additions & 77 deletions interfaces/IMerkleDistributor.sol

This file was deleted.

7 changes: 0 additions & 7 deletions interfaces/IOwnable.sol

This file was deleted.

8 changes: 0 additions & 8 deletions interfaces/IProxy.sol

This file was deleted.

29 changes: 0 additions & 29 deletions interfaces/IRewards.sol

This file was deleted.

Empty file added reporter/__init__.py
Empty file.
70 changes: 70 additions & 0 deletions reporter/account.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
from enum import Enum
from tinydb import Query

Account = Query()


class AccountState(Enum):
ACTIVE = "active"
INACTIVE = "inactive"
SLASHED = "slashed"


def to_staker(staker):
return {"address": staker[0], "balance": int(staker[1])}


def state(voted, inactive_windows):
if voted:
return AccountState.ACTIVE.value

# check if inactive windows are consecutive

end_slice = inactive_windows[-3:]

slashed = len(end_slice) == 3 and sorted(end_slice) == list(
range(min(end_slice), max(end_slice) + 1)
)

return AccountState.INACTIVE.value if not slashed else AccountState.SLASHED.value


def init_account(staker, db, window_index, participation, claim_map):
account_prev = db.table("accounts").get(Account.address == staker["address"])
voted = participation[staker["address"]]
claimed = claim_map[staker["address"]]

if account_prev == None:
# new staker, need to init his account
inactive_windows = [] if voted else [window_index]

return {
"address": staker["address"],
"vedough_balance": int(staker["balance"]),
"slice_amount": 0,
"state": state(voted, inactive_windows),
"inactive_windows": inactive_windows,
}
else:
inactive_windows = (
account_prev["inactive_windows"]
if voted
else account_prev["inactive_windows"] + [window_index]
)

return {
"address": staker["address"],
"vedough_balance": int(staker["balance"]),
"slice_amount": 0 if claimed else account_prev["slice_amount"],
"state": state(voted, inactive_windows),
"inactive_windows": inactive_windows,
}


def update_account_with_distribution(account, rewards):
if account["state"] == AccountState.SLASHED.value:
account["slice_amount"] = 0
else:
account["slice_amount"] += rewards[account["address"]]

return account
32 changes: 32 additions & 0 deletions reporter/conf_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from pathlib import Path
from helpers import get_dates

import json


def gen():
(date, start_date, end_date) = get_dates()

block_snapshot = int(input("🔗 What is snapshot block? "))

distribution_window = int(input("#️⃣ What is the distribution window? "))

slice_to_distribute = input("🤑 What is the number of SLICE units to distribute? ")

conf = {
"date": f"{date.year}-{date.month}",
"start_timestamp": int(start_date.timestamp()),
"end_timestamp": int(end_date.timestamp()),
"block_snapshot": block_snapshot,
"distribution_window": distribution_window,
"slice_to_distribute": slice_to_distribute,
}

path = f"reports/{date.year}-{date.month}"

Path(path).mkdir(parents=True, exist_ok=True)
Path(f"{path}/csv/").mkdir(parents=True, exist_ok=True)
Path(f"{path}/json/").mkdir(parents=True, exist_ok=True)

conf_json_file = open(f"{path}/epoch-conf.json", "w+")
conf_json_file.write(json.dumps(conf, indent=4))
Loading

0 comments on commit 272486b

Please sign in to comment.