From b8f57c342307cfb11d1cb1c98c0301e236cd1783 Mon Sep 17 00:00:00 2001
From: Mathias Scherer <mathias@aragon.org>
Date: Thu, 14 Oct 2021 10:05:30 +0200
Subject: [PATCH] dev2master (#225)

* App: Support new subscriptions module (#220)

* app: support new subscriptions module

* shared: add fs import on artifacts

* shared: fix artifacts paths generation

* chore: changed URLs from *.eth.aragon.network to *.backend.aragon.org

* removing heartbeat (#223)

* removing heartbeat

* [cicd] updates github container registry
removes deployment step

Co-authored-by: Mathias Scherer <scherer.mat@gmail.com>

* [cicd] adds workflow for rinkeby image (#224)

Co-authored-by: Facu Spagnuolo <facuspagnuolo@users.noreply.github.com>
Co-authored-by: Martynas Prokopas <promaty@gmail.com>
Co-authored-by: Ramon Canales <emaildoramon@gmail.com>
---
 .github/workflows/mainnet.yml                 | 19 +---
 .github/workflows/rinkeby.yml                 | 39 ++++++++
 .github/workflows/testnet.yml                 | 28 +-----
 package.json                                  | 13 ++-
 packages/app/README.md                        |  8 +-
 packages/app/src/actions/court.js             |  4 -
 packages/app/src/actions/subscriptions.js     | 93 ++-----------------
 packages/app/src/actions/types.js             |  3 -
 packages/app/src/components/core/App.react.js |  2 -
 .../app/src/components/core/Navbar.react.js   |  1 -
 .../src/components/court/CourtConfig.react.js |  4 -
 .../subscriptions/PeriodDetail.react.js       |  5 +-
 .../subscriptions/PeriodsList.react.js        |  8 +-
 .../subscriptions/SubscribersList.react.js    | 72 --------------
 packages/app/src/reducers/subscriptions.js    |  4 +-
 packages/services/config.js                   |  9 --
 packages/shared/models/Court.js               | 51 ++--------
 .../models/artifacts/DynamicArtifacts.js      | 24 +++--
 packages/shared/package.json                  | 11 +--
 yarn.lock                                     | 41 +++++++-
 20 files changed, 135 insertions(+), 304 deletions(-)
 create mode 100644 .github/workflows/rinkeby.yml
 delete mode 100644 packages/app/src/components/subscriptions/SubscribersList.react.js

diff --git a/.github/workflows/mainnet.yml b/.github/workflows/mainnet.yml
index 5d50c97f..fc78110a 100644
--- a/.github/workflows/mainnet.yml
+++ b/.github/workflows/mainnet.yml
@@ -5,14 +5,14 @@ on:
     - v*
 env:
   # This is a base repository and we use ${GITHUB_REF##*/} to set the version of the container
-  REPO: docker.pkg.github.com/aragonone/court-backend/mainnet
+  REPO: ghcr.io/aragonone/court-backend/mainnet
 
 jobs:
   build:
     runs-on: ubuntu-latest
     steps:
     - uses: actions/checkout@v2
-    - run: docker login docker.pkg.github.com -u $GITHUB_ACTOR -p ${{secrets.GITHUB_TOKEN}}
+    - run: docker login ghcr.io -u $GITHUB_ACTOR -p ${{secrets.GITHUB_TOKEN}}
     - run: .github/scripts/docker-build.sh $REPO ${GITHUB_SHA}
 
   test:
@@ -20,7 +20,7 @@ jobs:
     needs: build
     steps:
     - uses: actions/checkout@v2
-    - run: docker login docker.pkg.github.com -u $GITHUB_ACTOR -p ${{secrets.GITHUB_TOKEN}}
+    - run: docker login ghcr.io -u $GITHUB_ACTOR -p ${{secrets.GITHUB_TOKEN}}
     - run: .github/scripts/test/run.sh $REPO:${GITHUB_SHA}
 
   release:
@@ -28,17 +28,6 @@ jobs:
     needs: test
     steps:
     - uses: actions/checkout@v2
-    - run: docker login docker.pkg.github.com -u $GITHUB_ACTOR -p ${{secrets.GITHUB_TOKEN}}
+    - run: docker login ghcr.io -u $GITHUB_ACTOR -p ${{secrets.GITHUB_TOKEN}}
     - run: .github/scripts/docker-release.sh $REPO:${GITHUB_SHA} $REPO:${GITHUB_REF##*/}
     - run: .github/scripts/docker-release.sh $REPO:${GITHUB_SHA} $REPO:latest
-
-  deploy:
-    runs-on: ubuntu-latest
-    needs: release
-    steps:
-    - uses: actions/checkout@v2
-    - run: .github/scripts/kubectl-config.sh ${{secrets.KUBE_CA}} ${{secrets.KUBE_SERVER}} ${{secrets.KUBE_TOKEN}}
-    - run: .github/scripts/kubectl-set-image.sh court-backend $REPO:${GITHUB_REF##*/}
-    - run: .github/scripts/kubectl-wait-ready.sh court-backend
-    # wait 10 sec for k8s to reroute ingress and check app endpoint
-    - run: sleep 10 && curl --fail https://court-backend-app.eth.aragon.network
diff --git a/.github/workflows/rinkeby.yml b/.github/workflows/rinkeby.yml
new file mode 100644
index 00000000..8f8187b5
--- /dev/null
+++ b/.github/workflows/rinkeby.yml
@@ -0,0 +1,39 @@
+name: Testnet CI/CD
+on:
+  push:
+    branches:
+    # Executes on any non master commit, but release and deploy steps only run on development
+    # This is useful to see if tests are passing during PR review
+    - '**'
+    - '!master'
+    paths-ignore:
+    - 'monitoring/**'
+    - 'emails/**'
+env:
+  # This is a base repository and we use ${GITHUB_SHA} to set the version of the container
+  REPO: docker.pkg.github.com/aragonone/court-backend/rinkeby
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v2
+    - run: docker login docker.pkg.github.com -u $GITHUB_ACTOR -p ${{secrets.GITHUB_TOKEN}}
+    - run: .github/scripts/docker-build.sh $REPO ${GITHUB_SHA}
+
+  test:
+    runs-on: ubuntu-latest
+    needs: build
+    steps:
+    - uses: actions/checkout@v2
+    - run: docker login docker.pkg.github.com -u $GITHUB_ACTOR -p ${{secrets.GITHUB_TOKEN}}
+    - run: .github/scripts/test/run.sh $REPO:${GITHUB_SHA}
+
+  release:
+    if: github.ref == 'refs/heads/development'
+    runs-on: ubuntu-latest
+    needs: test
+    steps:
+    - uses: actions/checkout@v2
+    - run: docker login docker.pkg.github.com -u $GITHUB_ACTOR -p ${{secrets.GITHUB_TOKEN}}
+    - run: .github/scripts/docker-release.sh $REPO:${GITHUB_SHA} $REPO:latest
diff --git a/.github/workflows/testnet.yml b/.github/workflows/testnet.yml
index b7b4d7c4..b0f36d82 100644
--- a/.github/workflows/testnet.yml
+++ b/.github/workflows/testnet.yml
@@ -11,14 +11,14 @@ on:
     - 'emails/**'
 env:
   # This is a base repository and we use ${GITHUB_SHA} to set the version of the container
-  REPO: docker.pkg.github.com/aragonone/court-backend/testnet
+  REPO: ghcr.io/aragonone/court-backend/testnet
 
 jobs:
   build:
     runs-on: ubuntu-latest
     steps:
     - uses: actions/checkout@v2
-    - run: docker login docker.pkg.github.com -u $GITHUB_ACTOR -p ${{secrets.GITHUB_TOKEN}}
+    - run: docker login ghcr.io -u $GITHUB_ACTOR -p ${{secrets.GITHUB_TOKEN}}
     - run: .github/scripts/docker-build.sh $REPO ${GITHUB_SHA}
 
   test:
@@ -26,7 +26,7 @@ jobs:
     needs: build
     steps:
     - uses: actions/checkout@v2
-    - run: docker login docker.pkg.github.com -u $GITHUB_ACTOR -p ${{secrets.GITHUB_TOKEN}}
+    - run: docker login ghcr.io -u $GITHUB_ACTOR -p ${{secrets.GITHUB_TOKEN}}
     - run: .github/scripts/test/run.sh $REPO:${GITHUB_SHA}
 
   release:
@@ -35,23 +35,5 @@ jobs:
     needs: test
     steps:
     - uses: actions/checkout@v2
-    - run: docker login docker.pkg.github.com -u $GITHUB_ACTOR -p ${{secrets.GITHUB_TOKEN}}
-    - run: .github/scripts/docker-release.sh $REPO:${GITHUB_SHA} $REPO:latest
-
-  deploy:
-    if: github.ref == 'refs/heads/development'
-    runs-on: ubuntu-latest
-    needs: release
-    steps:
-    - uses: actions/checkout@v2
-    - run: .github/scripts/kubectl-config.sh ${{secrets.KUBE_CA}} ${{secrets.KUBE_SERVER}} ${{secrets.KUBE_TOKEN}}
-    - run: .github/scripts/kubectl-set-image.sh court-backend-rinkeby $REPO:${GITHUB_SHA}
-    - run: .github/scripts/kubectl-set-image.sh court-backend-staging $REPO:${GITHUB_SHA}
-    - run: .github/scripts/kubectl-set-image.sh court-backend-ropsten $REPO:${GITHUB_SHA}
-    - run: .github/scripts/kubectl-wait-ready.sh court-backend-rinkeby
-    - run: .github/scripts/kubectl-wait-ready.sh court-backend-staging
-    - run: .github/scripts/kubectl-wait-ready.sh court-backend-ropsten
-    # wait 10 sec for k8s to reroute ingress and check app endpoint
-    - run: sleep 10 && curl --fail https://court-backend-app-rinkeby.eth.aragon.network
-    - run: curl --fail https://court-backend-app-staging.eth.aragon.network
-    - run: curl --fail https://court-backend-app-ropsten.eth.aragon.network
+    - run: docker login ghcr.io -u $GITHUB_ACTOR -p ${{secrets.GITHUB_TOKEN}}
+    - run: .github/scripts/docker-release.sh $REPO:${GITHUB_SHA} $REPO:latest
\ No newline at end of file
diff --git a/package.json b/package.json
index 5c258c4b..68dd15af 100644
--- a/package.json
+++ b/package.json
@@ -3,9 +3,6 @@
   "author": "Aragon One",
   "license": "GPL-3.0-or-later",
   "private": true,
-  "workspaces": [
-    "packages/*"
-  ],
   "devDependencies": {
     "lerna": "^3.14.1"
   },
@@ -17,5 +14,15 @@
     "start:services:dev": "lerna run --scope=@aragonone/court-backend-services --concurrency=1 --stream start:dev",
     "test:server": "lerna run --scope=@aragonone/court-backend-server --concurrency=1 --stream test",
     "test:services": "lerna run --scope=@aragonone/court-backend-services --concurrency=1 --stream test"
+  },
+  "workspaces": {
+    "packages": [
+      "packages/*"
+    ],
+    "nohoist": [
+      "@aragon/minime",
+      "@aragon/court",
+      "@aragonone/precedence-campaign-arbitrable"
+    ]
   }
 }
diff --git a/packages/app/README.md b/packages/app/README.md
index 40d0b573..c0bbd519 100644
--- a/packages/app/README.md
+++ b/packages/app/README.md
@@ -6,10 +6,10 @@ This is a React app that aims to serve a UI to read data from an Aragon Court in
 
 You can find the following deployed instances
 
-1. [Mainnet](https://court-backend-app.eth.aragon.network/)
-1. [Rinkeby](https://court-backend-app-rinkeby.eth.aragon.network/)
-1. [Staging](https://court-backend-app-staging.eth.aragon.network/)
-1. [Ropsten](https://court-backend-app-ropsten.eth.aragon.network/)
+1. [Mainnet](https://court-app.backend.aragon.org/)
+1. [Rinkeby](https://court-app-rinkeby.backend.aragon.org/)
+1. [Staging](https://court-app-staging.backend.aragon.org/)
+1. [Ropsten](https://court-app-ropsten.backend.aragon.org/)
 
 ### Setup
 
diff --git a/packages/app/src/actions/court.js b/packages/app/src/actions/court.js
index ce0e36b8..1f4a61c2 100644
--- a/packages/app/src/actions/court.js
+++ b/packages/app/src/actions/court.js
@@ -56,12 +56,8 @@ const CourtActions = {
             subscriptions {
               id
               currentPeriod
-              feeAmount
               feeToken
               periodDuration
-              prePaymentPeriods
-              resumePrePaidPeriods
-              latePaymentPenaltyPct
               governorSharePct
             }
             modules {
diff --git a/packages/app/src/actions/subscriptions.js b/packages/app/src/actions/subscriptions.js
index b2c94c86..db21db11 100644
--- a/packages/app/src/actions/subscriptions.js
+++ b/packages/app/src/actions/subscriptions.js
@@ -11,8 +11,10 @@ const SubscriptionsActions = {
           subscriptionPeriod (id: "${id}") {
             id
             feeToken
-            feeAmount
             collectedFees
+            balanceCheckpoint
+            totalActiveBalance
+            accumulatedGovernorFees
             jurorClaims {
               id
               juror { id }
@@ -23,19 +25,6 @@ const SubscriptionsActions = {
 
         const { subscriptionPeriod: period } = result
         dispatch(SubscriptionsActions.receivePeriod(period))
-
-        const courtAddress = await CourtActions.findCourt()
-        if (await Network.isCourtAt(courtAddress)) {
-          const court = await Network.getCourt(courtAddress)
-          const { balanceCheckpoint, totalActiveBalance } = await court.getPeriod(id)
-          period.balanceCheckpoint = balanceCheckpoint
-          period.totalActiveBalance = totalActiveBalance
-        } else {
-          period.balanceCheckpoint = 'cannot be fetched'
-          period.totalActiveBalance = 'cannot be fetched'
-        }
-
-        dispatch(SubscriptionsActions.receivePeriod(period))
       } catch(error) {
         dispatch(ErrorActions.show(error))
       }
@@ -49,86 +38,22 @@ const SubscriptionsActions = {
           subscriptionPeriods (orderBy: createdAt, orderDirection: desc) {
             id
             feeToken
-            feeAmount
             collectedFees
+            balanceCheckpoint
+            totalActiveBalance
+            accumulatedGovernorFees
+            createdAt
           }
         }`)
 
         const { subscriptionPeriods: periods } = result
         dispatch(SubscriptionsActions.receiveAllPeriods(periods))
-
-        const courtAddress = await CourtActions.findCourt()
-        if (await Network.isCourtAt(courtAddress)) {
-          const court = await Network.getCourt(courtAddress)
-          for (const period of periods) {
-            const { balanceCheckpoint, totalActiveBalance } = await court.getPeriod(period.id)
-            period.balanceCheckpoint = balanceCheckpoint
-            period.totalActiveBalance = totalActiveBalance
-            dispatch(SubscriptionsActions.receiveAllPeriods(periods))
-          }
-        } else {
-          for (const period of periods) {
-            period.balanceCheckpoint = 'cannot be fetched'
-            period.totalActiveBalance = 'cannot be fetched'
-          }
-          dispatch(SubscriptionsActions.receiveAllPeriods(periods))
-        }
-      } catch(error) {
-        dispatch(ErrorActions.show(error))
-      }
-    }
-  },
-
-  findAllSubscribers() {
-    return async function(dispatch) {
-      try {
-        const result = await Network.query(`{
-          subscribers {
-            id
-            subscribed
-            paused
-            lastPaymentPeriodId
-            previousDelayedPeriods
-          }
-        }`)
-        dispatch(SubscriptionsActions.receiveAllSubscribers(result.subscribers))
       } catch(error) {
         dispatch(ErrorActions.show(error))
       }
     }
   },
 
-  findModule() {
-    return async function(dispatch) {
-      try {
-        const result = await Network.query(`{
-          subscriptionModules (first: 1) {
-            id
-            currentPeriod
-            feeAmount
-            feeToken
-            periodDuration
-            prePaymentPeriods
-            resumePrePaidPeriods
-            latePaymentPenaltyPct
-            governorSharePct
-            totalPaid
-            totalDonated
-            totalCollected
-            totalGovernorShares
-          }
-        }`)
-        dispatch(SubscriptionsActions.receiveModule(result.subscriptionModules[0]))
-      } catch(error) {
-        dispatch(ErrorActions.show(error))
-      }
-    }
-  },
-
-  receiveAllSubscribers(list) {
-    return { type: ActionTypes.RECEIVE_SUBSCRIBERS, list }
-  },
-
   receiveAllPeriods(list) {
     return { type: ActionTypes.RECEIVE_SUBSCRIPTION_PERIODS, list }
   },
@@ -136,10 +61,6 @@ const SubscriptionsActions = {
   receivePeriod(period) {
     return { type: ActionTypes.RECEIVE_SUBSCRIPTION_PERIOD, period }
   },
-
-  receiveModule(module) {
-    return { type: ActionTypes.RECEIVE_SUBSCRIPTION_MODULE, module }
-  },
 }
 
 export default SubscriptionsActions
diff --git a/packages/app/src/actions/types.js b/packages/app/src/actions/types.js
index b1b858db..93a27f05 100644
--- a/packages/app/src/actions/types.js
+++ b/packages/app/src/actions/types.js
@@ -33,15 +33,12 @@ export const RECEIVE_DRAFTS_LIST = 'RECEIVE_DRAFTS_LIST'
 export const RECEIVE_TRANSFERS = 'RECEIVE_TRANSFERS'
 export const RECEIVE_BALANCES = 'RECEIVE_BALANCES'
 
-export const RECEIVE_SUBSCRIBERS = 'RECEIVE_SUBSCRIBERS'
 export const RECEIVE_SUBSCRIPTION_MODULE = 'RECEIVE_SUBSCRIPTION_MODULE'
 export const RECEIVE_SUBSCRIPTION_PERIOD = 'RECEIVE_SUBSCRIPTION_PERIOD'
 export const RECEIVE_SUBSCRIPTION_PERIODS = 'RECEIVE_SUBSCRIPTION_PERIODS'
 
 export const RECEIVE_ADMIN = 'RECEIVE_ADMIN'
 export const RESET_ADMIN = 'RESET_ADMIN'
-export const RECEIVE_ADMIN_TOKEN = 'RECEIVE_ADMIN_TOKEN'
-export const RESET_ADMIN_TOKEN = 'RESET_ADMIN_TOKEN'
 export const RECEIVE_ADMIN_ADMINS = 'RECEIVE_ADMIN_ADMINS'
 export const RECEIVE_ADMIN_REVEALS = 'RECEIVE_ADMIN_REVEALS'
 export const RECEIVE_ADMIN_USERS = 'RECEIVE_ADMIN_USERS'
diff --git a/packages/app/src/components/core/App.react.js b/packages/app/src/components/core/App.react.js
index 89ae23bf..9efcad5d 100644
--- a/packages/app/src/components/core/App.react.js
+++ b/packages/app/src/components/core/App.react.js
@@ -13,7 +13,6 @@ import RevealsList from '../admin/RevealsList.react'
 import EmailsForm from '../admin/EmailsForm.react'
 import PeriodsList from '../subscriptions/PeriodsList.react'
 import PeriodDetail from '../subscriptions/PeriodDetail.react'
-import SubscribersList from '../subscriptions/SubscribersList.react'
 import JurorsList from '../jurors/JurorsList.react'
 import JurorDetail from '../jurors/JurorDetail.react'
 import JurorDraftsList from '../jurors/JurorDraftsList.react'
@@ -55,7 +54,6 @@ class App extends React.Component {
             <Route path="/jurors/:address/staking" component={JurorStakingList}/>
             <Route path="/jurors/:address/accounting" component={JurorAccountingList}/>
             <Route path="/drafts/" component={DraftsList}/>
-            <Route path="/subscribers/" component={SubscribersList}/>
             <Route path="/periods/" component={PeriodsList}/>
             <Route path="/period/:id" component={PeriodDetail}/>
             <Route path="/anj-balances/" exact component={ANJBalancesList}/>
diff --git a/packages/app/src/components/core/Navbar.react.js b/packages/app/src/components/core/Navbar.react.js
index 66747e9d..9f16d58a 100644
--- a/packages/app/src/components/core/Navbar.react.js
+++ b/packages/app/src/components/core/Navbar.react.js
@@ -23,7 +23,6 @@ export default class Navbar extends React.Component {
           <Link to="/disputes">Disputes</Link>
           <Link to="/jurors">Jurors</Link>
           <Link to="/drafts">Drafts</Link>
-          <Link to="/subscribers">Subscribers</Link>
           <Link to="/periods">Periods</Link>
           <Link to="/anj-balances">ANJ</Link>
           { admin.id && this._buildLoggedInItems() }
diff --git a/packages/app/src/components/court/CourtConfig.react.js b/packages/app/src/components/court/CourtConfig.react.js
index 3da9758c..8d03801e 100644
--- a/packages/app/src/components/court/CourtConfig.react.js
+++ b/packages/app/src/components/court/CourtConfig.react.js
@@ -98,11 +98,7 @@ export default class CourtConfig extends React.Component {
         <h3>Subscriptions</h3>
         <p>Current period: {subscriptions.currentPeriod}</p>
         <p>Period duration: {subscriptions.periodDuration} court terms</p>
-        <p>Fee amount: {fromWei(subscriptions.feeAmount.toString())}</p>
         <p>Fee token: {subscriptions.feeToken}</p>
-        <p>Pre payment periods: {subscriptions.prePaymentPeriods}</p>
-        <p>Resume pre paid periods: {subscriptions.resumePrePaidPeriods}</p>
-        <p>Late payment penalty pct: % {subscriptions.latePaymentPenaltyPct}</p>
         <p>Governor share pct: % {subscriptions.governorSharePct}</p>
 
         <h3>Modules</h3>
diff --git a/packages/app/src/components/subscriptions/PeriodDetail.react.js b/packages/app/src/components/subscriptions/PeriodDetail.react.js
index 409106fc..5802a4a9 100644
--- a/packages/app/src/components/subscriptions/PeriodDetail.react.js
+++ b/packages/app/src/components/subscriptions/PeriodDetail.react.js
@@ -22,9 +22,8 @@ export default class PeriodDetail extends React.Component {
           { !period.id ? 'Loading...' :
             <div>
               <p>Fee token: {period.feeToken}</p>
-              <p>Fee amount: {fromWei(period.feeAmount)}</p>
-              <p>Balance checkpoint: {period.balanceCheckpoint ? period.balanceCheckpoint : 'loading...'}</p>
-              <p>Total active balance: {period.totalActiveBalance ? fromWei(period.totalActiveBalance) : 'loading...'}</p>
+              <p>Balance checkpoint: {period.balanceCheckpoint}</p>
+              <p>Total active balance: {period.totalActiveBalance}</p>
               <p>Collected fees: {fromWei(period.collectedFees)}</p>
               <p>Claims: {this.state.period.jurorClaims.length === 0 && 'None'}</p>
               <ul>{this._buildClaimsList()}</ul>
diff --git a/packages/app/src/components/subscriptions/PeriodsList.react.js b/packages/app/src/components/subscriptions/PeriodsList.react.js
index 76c93b0d..f4edc5fe 100644
--- a/packages/app/src/components/subscriptions/PeriodsList.react.js
+++ b/packages/app/src/components/subscriptions/PeriodsList.react.js
@@ -27,10 +27,9 @@ export default class PeriodsList extends React.Component {
               <tr>
                 <th>ID</th>
                 <th>Fee token</th>
-                <th>Fee amount</th>
+                <th>Collected fees</th>
                 <th>Balance checkpoint</th>
                 <th>Total active balance</th>
-                <th>Collected fees</th>
               </tr>
             </thead>
             <tbody>
@@ -52,10 +51,9 @@ export default class PeriodsList extends React.Component {
             </Link>
           </td>
           <td>{period.feeToken}</td>
-          <td>{fromWei(period.feeAmount)}</td>
-          <td>{period.balanceCheckpoint ? period.balanceCheckpoint : 'loading...'}</td>
-          <td>{period.totalActiveBalance ? fromWei(period.totalActiveBalance) : 'loading...'}</td>
           <td>{fromWei(period.collectedFees)}</td>
+          <td>{period.balanceCheckpoint}</td>
+          <td>{period.totalActiveBalance}</td>
         </tr>
       )
     })
diff --git a/packages/app/src/components/subscriptions/SubscribersList.react.js b/packages/app/src/components/subscriptions/SubscribersList.react.js
deleted file mode 100644
index 700a2161..00000000
--- a/packages/app/src/components/subscriptions/SubscribersList.react.js
+++ /dev/null
@@ -1,72 +0,0 @@
-import React from 'react'
-import Store from '../../store/store'
-import { fromWei } from 'web3-utils'
-import SubscriptionsActions from '../../actions/subscriptions'
-
-export default class SubscribersList extends React.Component {
-  constructor(props){
-    super(props)
-    this.state = { subscribers: null, module: null }
-  }
-
-  componentDidMount() {
-    Store.subscribe(() => this._onChange())
-    Store.dispatch(SubscriptionsActions.findModule())
-    Store.dispatch(SubscriptionsActions.findAllSubscribers())
-  }
-
-  render() {
-    const { subscribers, module } = this.state
-    return (
-      <div ref="subscribersList">
-        <h3>Subscribers</h3>
-        { (!subscribers || !module) ? <em>Loading...</em> : subscribers.length === 0 ?
-          <em>None</em> :
-          <div>
-            <div>
-              <p>Total paid: {fromWei(module.totalPaid)} </p>
-              <p>Total donated: {fromWei(module.totalDonated)} </p>
-              <p>Total collected: {fromWei(module.totalCollected)} </p>
-              <p>Total governor shares: {fromWei(module.totalGovernorShares)} </p>
-            </div>
-            <table>
-              <thead>
-                <tr>
-                  <th>ID</th>
-                  <th>Subscribed</th>
-                  <th>Paused</th>
-                  <th>Last period ID</th>
-                  <th>Previous delayed periods</th>
-                </tr>
-              </thead>
-              <tbody>
-                {this._buildList()}
-              </tbody>
-            </table>
-          </div>
-        }
-      </div>
-    )
-  }
-
-  _buildList() {
-    return this.state.subscribers.map((subscriber, index) => {
-      return (
-        <tr key={index}>
-          <td><b>{subscriber.id}</b></td>
-          <td>{subscriber.subscribed ? 'Yes' : 'No'} </td>
-          <td>{subscriber.paused ? 'Yes' : 'No'}</td>
-          <td>{subscriber.lastPaymentPeriodId}</td>
-          <td>{subscriber.previousDelayedPeriods}</td>
-        </tr>
-      )
-    })
-  }
-
-  _onChange() {
-    if(this.refs.subscribersList) {
-      const { module, subscribers } = Store.getState().subscriptions
-      this.setState({ module, subscribers })
-    }
-  }
-}
diff --git a/packages/app/src/reducers/subscriptions.js b/packages/app/src/reducers/subscriptions.js
index 956fcc25..e8bb529b 100644
--- a/packages/app/src/reducers/subscriptions.js
+++ b/packages/app/src/reducers/subscriptions.js
@@ -1,6 +1,6 @@
 import * as ActionTypes from '../actions/types'
 
-const initialState = { module: null, subscribers: [], periods: [], period: {} }
+const initialState = { module: null, periods: [], period: {} }
 
 const SubscriptionsReducer = (state = initialState, action) => {
   switch (action.type) {
@@ -10,8 +10,6 @@ const SubscriptionsReducer = (state = initialState, action) => {
       return Object.assign({}, state, { period: action.period })
     case ActionTypes.RECEIVE_SUBSCRIPTION_PERIODS:
       return Object.assign({}, state, { periods: action.list })
-    case ActionTypes.RECEIVE_SUBSCRIBERS:
-      return Object.assign({}, state, { subscribers: action.list })
     default:
       return state
   }
diff --git a/packages/services/config.js b/packages/services/config.js
index 3fcb2729..61eba9e3 100644
--- a/packages/services/config.js
+++ b/packages/services/config.js
@@ -1,15 +1,6 @@
 const ONE_MINUTE = 60
 
 const workers = [
-  {
-    name: 'heartbeat',
-    color: 'yellow',
-    path: './src/workers/heartbeat',
-    processes: 1,
-    times: 0,
-    repeat: ONE_MINUTE * 10,
-    metricsPort: process.env.SERVICE_PORT_HEARTBEAT
-  },
   {
     name: 'reveal',
     color: 'pink',
diff --git a/packages/shared/models/Court.js b/packages/shared/models/Court.js
index a1ad64bf..7a1f8d80 100644
--- a/packages/shared/models/Court.js
+++ b/packages/shared/models/Court.js
@@ -1,11 +1,10 @@
 const logger = require('../helpers/logger')('Court')
 const { bn, bigExp } = require('../helpers/numbers')
-const { decodeEventsOfType } = require('@aragon/court/test/helpers/lib/decodeEvent')
 const { encodeVoteId, hashVote } = require('../helpers/voting')
 const { DISPUTE_MANAGER_EVENTS } = require('@aragon/court/test/helpers/utils/events')
 const { DISPUTE_MANAGER_ERRORS } = require('@aragon/court/test/helpers/utils/errors')
-const { getEventArgument, getEvents } = require('@aragon/test-helpers/events')
-const { sha3, fromWei, utf8ToHex, soliditySha3, padLeft, toHex } = require('web3-utils')
+const { getEventArgument, getEvents, decodeEvents } = require('@aragon/contract-helpers-test')
+const { sha3, fromWei, utf8ToHex, soliditySha3 } = require('web3-utils')
 
 const ROUND_STATE_ENDED = 5
 const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'
@@ -174,45 +173,8 @@ module.exports = class {
 
   async getPeriod(periodId) {
     const subscriptions = await this.subscriptions()
-    const provider = await this.environment.getProvider()
-
-    // The period records are stored at the 7th index of the subscriptions contract storage
-    const periodsRecordsSlot = padLeft(7, 64)
-    // Parse period ID en hexadecimal and pad 64
-    const periodIdHex = padLeft(toHex(periodId), 64)
-    // The periods records variable is a mapping indexed by period IDs
-    const periodsSlot = soliditySha3(periodIdHex + periodsRecordsSlot.slice(2))
-    // The checkpoint and fee token are packed in the first element of the period struct, thus don't need to add any offset
-    const checkpointAndFeeTokenSlot = periodsSlot
-    // The fee amount is the second element of the struct, thus we add 1 to the period slot
-    const feeAmountSlot = bn(periodsSlot).add(bn(1)).toHexString()
-    // The total active balance is the third element of the struct, thus we add 2 to the period slot
-    const totalActiveBalanceSlot = bn(periodsSlot).add(bn(2)).toHexString()
-    // The collected fees is the fourth element of the struct, thus we add 3 to the period slot
-    const collectedFeesSlot = bn(periodsSlot).add(bn(3)).toHexString()
-
-
-    // The first part of the checkpoint and fee token slot is for the fee token
-    const checkpointAndFeeToken = await provider.getStorageAt(subscriptions.address, checkpointAndFeeTokenSlot)
-    const feeToken = `0x${checkpointAndFeeToken.substr(10, 40)}`
-
-    // The balance checkpoint is stored using a uint64 and its stored at the end of the slot
-    const rawBalanceCheckpoint = checkpointAndFeeToken.substr(50)
-    const balanceCheckpoint = bn(`0x${rawBalanceCheckpoint}`).toString()
-
-    // Parse the fee amount
-    const rawFeeAmount = await provider.getStorageAt(subscriptions.address, feeAmountSlot)
-    const feeAmount = bn(rawFeeAmount).toString()
-
-    // Parse the total active balance
-    const rawTotalActiveBalance = await provider.getStorageAt(subscriptions.address, totalActiveBalanceSlot)
-    const totalActiveBalance = bn(rawTotalActiveBalance).toString()
-
-    // Parse the collected fees
-    const rawCollectedFees = await provider.getStorageAt(subscriptions.address, collectedFeesSlot)
-    const collectedFees = bn(rawCollectedFees).toString()
-
-    return { balanceCheckpoint, feeToken, feeAmount, totalActiveBalance, collectedFees }
+    const { feeToken, balanceCheckpoint, totalActiveBalance, collectedFees, accumulatedGovernorFees } = await subscriptions.getPeriod(periodId)
+    return { balanceCheckpoint, feeToken, totalActiveBalance, collectedFees, accumulatedGovernorFees }
   }
 
   async heartbeat(transitions = undefined) {
@@ -306,13 +268,12 @@ module.exports = class {
     const arbitrable = await Arbitrable.at(subject)
 
     const shouldCreateAndSubmit = evidence.length === 2 && submitters.length === 2
-    const { hash } = shouldCreateAndSubmit
+    const receipt = shouldCreateAndSubmit
       ? (await arbitrable.createAndSubmit(rulings, utf8ToHex(metadata), submitters[0], submitters[1], utf8ToHex(evidence[0]), utf8ToHex(evidence[1])))
       : (await arbitrable.createDispute(rulings, utf8ToHex(metadata)))
 
     const DisputeManager = await this.environment.getArtifact('DisputeManager', '@aragon/court')
-    const { logs: rawLogs } = await this.environment.getTransaction(hash)
-    const logs = decodeEventsOfType({ receipt: { rawLogs }}, DisputeManager.abi, DISPUTE_MANAGER_EVENTS.NEW_DISPUTE)
+    const logs = decodeEvents(receipt, DisputeManager.abi, DISPUTE_MANAGER_EVENTS.NEW_DISPUTE)
     const disputeId = getEventArgument({ logs }, DISPUTE_MANAGER_EVENTS.NEW_DISPUTE, 'disputeId')
 
     if (!shouldCreateAndSubmit) {
diff --git a/packages/shared/models/artifacts/DynamicArtifacts.js b/packages/shared/models/artifacts/DynamicArtifacts.js
index cc98a29f..4d593ee9 100644
--- a/packages/shared/models/artifacts/DynamicArtifacts.js
+++ b/packages/shared/models/artifacts/DynamicArtifacts.js
@@ -1,21 +1,29 @@
+const fs = require('fs')
 const path = require('path')
 const BaseArtifacts = require('./BaseArtifacts')
 
+const BUILD_DIRS = ['build/contracts', 'artifacts']
+
 class DynamicArtifacts extends BaseArtifacts {
   getContractSchema(contractName, dependency = undefined) {
-    const contractPath = dependency
-      ? this._getNodeModulesPath(dependency, contractName)
-      : this._getLocalBuildPath(contractName)
+    const contractPaths = dependency
+      ? this._getNodeModulesPaths(dependency, contractName)
+      : this._getLocalBuildPaths(contractName)
+
+    return this._findArtifact(contractPaths)
+  }
 
-    return require(contractPath)
+  _findArtifact(paths) {
+    const artifactPath = paths.find(fs.existsSync)
+    return artifactPath ? require(artifactPath) : undefined
   }
 
-  _getLocalBuildPath(contractName) {
-    return path.resolve(process.cwd(), `./build/contracts/${contractName}.json`)
+  _getLocalBuildPaths(contractName) {
+    return BUILD_DIRS.map(dir => path.resolve(process.cwd(), `./${dir}/${contractName}.json`))
   }
 
-  _getNodeModulesPath(dependency, contractName) {
-    return path.resolve(__dirname, `../../node_modules/${dependency}/build/contracts/${contractName}.json`)
+  _getNodeModulesPaths(dependency, contractName) {
+    return BUILD_DIRS.map(dir => path.resolve(__dirname, `../../node_modules/${dependency}/${dir}/${contractName}.json`))
   }
 }
 
diff --git a/packages/shared/package.json b/packages/shared/package.json
index 09b846e7..a90c75c3 100644
--- a/packages/shared/package.json
+++ b/packages/shared/package.json
@@ -2,19 +2,12 @@
   "name": "@aragonone/court-backend-shared",
   "version": "0.2.22",
   "private": true,
-  "workspaces": {
-    "nohoist": [
-      "@aragon/minime",
-      "@aragon/court",
-      "@aragonone/precedence-campaign-arbitrable"
-    ]
-  },
   "author": "Aragon One",
   "license": "(GPL-3.0-or-later OR AGPL-3.0-or-later)",
   "dependencies": {
-    "@aragon/court": "1.1.0",
+    "@aragon/court": "1.2.0-rc.0",
     "@aragon/minime": "1.0.0",
-    "@aragon/test-helpers": "^2.1.0",
+    "@aragon/contract-helpers-test": "^0.1.0",
     "@aragon/truffle-config-v5": "^1.0.1",
     "@aragonone/erc20-faucet": "^1.0.0",
     "@aragonone/precedence-campaign-arbitrable": "^1.0.0",
diff --git a/yarn.lock b/yarn.lock
index 8d8334f7..db70c31b 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2,11 +2,24 @@
 # yarn lockfile v1
 
 
+"@aragon/contract-helpers-test@^0.1.0":
+  version "0.1.0"
+  resolved "https://registry.yarnpkg.com/@aragon/contract-helpers-test/-/contract-helpers-test-0.1.0.tgz#5c5f09739a0b33ab66843bc4849cb2b997d88af0"
+  integrity sha512-xP2CqqP0Jw/6Jdo1mRg4OxL+3gmsCYV3EJRy7xN8xUrhQIqkOyRptC44X951O7Cr+VeqXJq22rpZSr01TJZhNg==
+  dependencies:
+    web3-eth-abi "1.2.5"
+    web3-utils "1.2.5"
+
 "@aragon/court@1.1.0":
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/@aragon/court/-/court-1.1.0.tgz#32bd7150d81946de26ed04b87fd730a98f63c741"
   integrity sha512-ccxsvGJEO8hbKeF3Q0m9d18z7DwW+PMLiafBx165p/FgTMRXmJsbHvE5sm5zwqUiPBNjh3rlmn1vp2DWCOotdA==
 
+"@aragon/court@1.2.0-rc.0":
+  version "1.2.0-rc.0"
+  resolved "https://registry.yarnpkg.com/@aragon/court/-/court-1.2.0-rc.0.tgz#dc3af0113dbfadda8d47ed516bb55a5be0964c9b"
+  integrity sha512-Vk3pmyifTNuQZGgt19yRojF8JnT/4mYEYJCjYlax6JaxX/7hc/PIX+oFPJhqj6vYml6mZFJrp4YJBVctw2zr+Q==
+
 "@aragon/court@^1.1.0":
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/@aragon/court/-/court-1.1.3.tgz#3ccc459abd9db8f390d2d1b158c2a57871dcb127"
@@ -17,11 +30,6 @@
   resolved "https://registry.yarnpkg.com/@aragon/minime/-/minime-1.0.0.tgz#994284b38e2ca36b5ae923f090928948041c928f"
   integrity sha512-FU6xQXkkRfaxo0n6HEnlsizSA12Te2bghozqW9XLdDbzpC84yDeqUOQAxmrHo5yQPIezRfz7IrNzpLVL6ZBORA==
 
-"@aragon/test-helpers@^2.1.0":
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/@aragon/test-helpers/-/test-helpers-2.1.0.tgz#5383d69a94b387c2c369f2496f290dd104fc2a4d"
-  integrity sha512-lL0seKOPBr0BDt5EiXQhevdXCtkJbv/ZFoJFLWmhlcdd8TKsWffj+c/hzYXP9l9OjCqPbO61JsDBPS0ZDF7ERA==
-
 "@aragon/truffle-config-v5@^1.0.1":
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/@aragon/truffle-config-v5/-/truffle-config-v5-1.0.1.tgz#e69803bca836357cfef55f3ff97b3b8593425d88"
@@ -16980,6 +16988,15 @@ web3-eth-abi@1.2.11, web3-eth-abi@^1.2.11:
     underscore "1.9.1"
     web3-utils "1.2.11"
 
+web3-eth-abi@1.2.5:
+  version "1.2.5"
+  resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.2.5.tgz#7ffddd3a3e7bacd66a2186e5e388310786b9b548"
+  integrity sha512-Tz6AjGTlgZVpv01h2YgotoXoQAQgWacx82Zh72ZlZ4iBCs4SoiYvq6tfbW9pquylK2Egm23bELsrSSENz0204w==
+  dependencies:
+    ethers "4.0.0-beta.3"
+    underscore "1.9.1"
+    web3-utils "1.2.5"
+
 web3-eth-accounts@1.2.1:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.2.1.tgz#2741a8ef337a7219d57959ac8bd118b9d68d63cf"
@@ -17267,6 +17284,20 @@ web3-utils@1.2.11, web3-utils@^1.2.4:
     underscore "1.9.1"
     utf8 "3.0.0"
 
+web3-utils@1.2.5:
+  version "1.2.5"
+  resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.2.5.tgz#7691f981ce11dc919e123edbde159dce061a5a53"
+  integrity sha512-U0tNfB4Hep5ouzvNZ+Hr8I8kIftiHiDhwg+Eoh2Nvr5lLOPEH14B2exkRSARLXGY9xl2p3ykJWBCKoG1oCadug==
+  dependencies:
+    bn.js "4.11.8"
+    eth-lib "0.2.7"
+    ethereum-bloom-filters "^1.0.6"
+    ethjs-unit "0.1.6"
+    number-to-bn "1.7.0"
+    randombytes "^2.1.0"
+    underscore "1.9.1"
+    utf8 "3.0.0"
+
 web3@*:
   version "1.2.11"
   resolved "https://registry.yarnpkg.com/web3/-/web3-1.2.11.tgz#50f458b2e8b11aa37302071c170ed61cff332975"