Skip to content

Instantly share code, notes, and snippets.

@izqui izqui/aragon.md Secret
Last active Jun 9, 2018

Embed
What would you like to do?
[WIP] your first aragon app

Your First Aragon App

==TODO: Screenshots to illustrate how to open your local app in the wrapper==

==TODO: Briefly explain how the ACL works and what a forwarder is, but leave the flesh for another post==

==TODO: Update aragon-example-application and link to it here==

With the release of Aragon 0.5, it is now possible to start tinkering and developing your own Aragon apps.

In this guide, we will walk you through creating your first Aragon app using aragonOS 3, Aragon CLI and Aragon.js.

Why Build An Aragon App?

Let's first talk a bit about why Aragon might be a fit for you, and why you might want to build an Aragon app.

If you're building a protocol with some sort of curation mechanism or community involvement, or a dapp, Aragon gives you two things virtually for free: upgradeability and governance.

There is a case to be made that upgradeability and governance are two sides of the same coin, since upgradeability without governance re-centralises authority in an otherwise decentralised system, because you have a single entity who decides what version of a contract you interact with.

On the flipside, governance without upgradeability creates rigid systems, which is bad, because what works today, might not work tomorrow.

The Setup

Let's first set up our project. You can do this manually, but we recommend that you install Aragon CLI already now, since you will need it later, and because it can bootstrap the project for you:

npm i -g @aragon/cli@2.2.0

We also need Truffle for compiling our smart contracts:

npm i -g truffle

Next, we bootstrap our project:

aragon init foo.aragonpm.eth bare

Notice that we input a fully qualified ENS name. We also initialise the app from the bare template, so that we don't get anything extra added, such as Aragon UI.

Let's examine the ENS name we entered, because it is not entirely arbitrary.

Illustration of foo.aragonpm.eth

The first label in the ENS name is the name of our app. This can be anything you want, given that the full ENS name is not taken.

The second and third label is the name of the APM (Aragon Package Manager) registry that your repository will be (or is) registered to. For the sake of simplicity, this guide assumes that you have rights to create repositories on aragonpm.eth, but you could deploy your own APM registry if you so desire.

Writing A Simple Contract

To illustrate how easy it is to use aragonOS, we will build our app as a trivial smart contract, without implementing any Aragon-specific interfaces at all.

Today, we will build a simple counter app - you can increment it, you can decrement it, and it will all be decentralised. Decentralised coffee counter, anyone?

pragma solidity 0.4.18;

contract Counter {
    // Events
    event Increment(address entity);
    event Decrement(address entity);

    // State
    int public value;
    
    function increment() external {
        value += 1;
        Increment(msg.sender);
    }

    function decrement() external {
        value -= 1;
        Decrement(msg.sender);
    }
}

Pretty simple, right? You might wonder why we would bother adding events to this smart contract, but it comes in handy later for illustration purposes - and we can also create an activity feed from it, if we wanted to.

3 Steps To Upgradeability And Governance

Now for the interesting part: making our simple smart contract an Aragon app.

First, inherit from the Aragon app smart contract, like so:

// ...
import "@aragon/os/contracts/apps/AragonApp.sol";

contract Counter is AragonApp {
    // ...
}

Second, define the roles that you want your app to have. A role can be assigned to other apps or people, and those entities will have access to methods guarded by that role.

In this example, we will define a role for incrementing, and a role for decrementing, but note that you can have a single role to guard all methods in your contract if you find that appropriate.

// ...

contract Counter is AragonApp {
    // ...
    bytes32 constant public INCREMENT_ROLE = keccak256("INCREMENT_ROLE");
    bytes32 constant public DECREMENT_ROLE = keccak256("DECREMENT_ROLE");
    // ...
}

Finally, guard the methods with the auth modifier that the AragonApp interface gives you:

// ...

contract Counter is AragonApp {
    // ...
    
    function increment() auth(INCREMENT_ROLE) external {
        // ...
    }

    function decrement() auth(DECREMENT_ROLE) external {
        // ...
    }
}

That's it. In 3 steps, you now have an Aragon app, with full upgradeability and modular governance.

Descriptive Transactions

==TODO: Maybe make this a separate post entirely==

A big part of Aragon is user-friendliness, and one of the most unfriendly things might be transaction data. Examine this screenshot of a transaction in MetaMask:

==TODO: Cryptokitties transaction screenshot==

Would you know what this transaction does? Not even a developer could tell (by the way, it buys a cryptokitty); this is why we created Radspec.

Radspec is a secure alternative to Natspec. Natspec was supposed to be a way to describe transactions from a Natspec expression and some transaction data.

The issue with Natspec, however, is that it is fully insecure. Any JavaScript goes in Natspec, which opens up a lot of potential attacks, like cross-site scripting, which might successfully phish users.

We will make a post about Radspec in more detail, but for now, let's add some simple descriptions to some transactions in our app.

// ...

contract Counter is AragonApp {
    // ...
    
    /**
     * @notice Increment the counter by 1
     */
    function increment() auth(INCREMENT_ROLE) external {
        // ...
    }

    /**
     * @notice Decrement the counter by 1
     */
    function decrement() auth(DECREMENT_ROLE) external {
        // ...
    }
}

These Radspec expressions are written in comments in your source code, and they will be grabbed by aragon and bundled with your app.

The wrapper will now display these alongside the transaction a user is about to perform, so that they have a clear understanding of what they're about to sign.

Obviously, this is a super trivial example as we are not actually evaluating anything, but we could instead write something like:

Decrement the counter by `(2 * 2) - 3`

which would evaluate to

Decrement the counter by 1

Building The Frontend

Building the front-end of your Aragon app is a bit different from building a normal dapp. This is because we have other security requirements, since we're essentially running code from other developers inside of our dapp, so in order to mitigate risk (such as cross-site scripting, phishing attempts by manipulating the DOM) we sandbox apps.

Because we sandbox apps, it also means that apps do not have direct access to Web3.

Apps are run inside a sandboxed iframe, which means that it only has access to its own DOM, not the outlying DOM. In order to perform transactions, calls, send notifications and so on, the app communicates with the wrapper over our own custom RPC protocol.

The wrapper takes care of connecting to Ethereum via Web3, and also handles things like signing transactions, displaying notifications and more to the end-user.

To make it a bit easier, we've developed a library we use internally called Aragon.js.

Aragon.js is split in two parts; one for wrappers and one for apps. The wrapper portion of Aragon.js reads requests from the app over RPC, sandboxes apps and performs Web3 actions, whereas the app portion of Aragon.js provides a simple API to communicate with the wrapper (to read state, send transactions and more).

Let's install Aragon.js for our app:

npm i @aragon/client

Background Workers And Building State

Apps usually want to listen to events using Web3 and build an application state from those events. This concept is also known as event sourcing.

Aragon.js was built with event sourcing in mind. To build state continually without having the app loaded indefinitely, though, we need to run a background script.

Thankfully wrappers will run background scripts specified in the manifest files of our app (more on manifest files later).

Let's start by writing a background worker that listens for our Increment and Decrement events, and builds a state that simply is the current value of our counter.

// src/worker.js
const Aragon = require('@aragon/client')

// Initialise the app
const app = new Aragon()

// Listen for events and reduce them to a state
const state$ = app.store((state, event) => {
  // Initial state
  if (state === null) state = 0
  
  // Build state
  switch (event.event) {
    case 'Decrement':
      state--
      break
    case 'Increment':
      state++
      break
  }
  
  return state
})

If you've worked with Redux before, this might look vaguely familiar.

The store method takes in a reducer function with the signature (state, event) => state, where state is whatever you want it to be (in this example it is an integer), and event is a Web3 event.

Internally, store will fetch the last known state (if any) and pass that in as the first argument, and then store the resulting state in cache. This state can be observed in the view portion of your app. Also note that the store method returns an observable of states. This is a recurring theme in Aragon.js; almost everything is an RxJS observable.

The reducer function must always return a state, even if it is the same state as before.

Note: The state will start out as null, not undefined because of the JSONRPC specification.

Displaying State

==TODO: Intents, sending transactions==

Now let's write the view portion of our app. In our case, this is a simple HTML file, and a simple JavaScript file that observes the state that our background worker builds for us.

<!-- app/index.html !-->
<!doctype html>
<html>
<head>
    <title>Counter App</title>
</head>
<body>
    <div id="view">...</div>
    <script src="app.js"></script>
</body>
</html>
// src/app.js
const Aragon = require('@aragon/client')

const app = new Aragon()
const view = document.getElementById('view')

app.state().subscribe(
  (state) => {
    view.innerHTML = `The counter is ${state}`
  },
  (err) => {
    view.innerHTML = 'An error occured, check the console'
    console.log(err)
  }
)

That's it! Internally, state observes the state key in cache and emits every time a change occurs.

The Build Script

Since we're importing Node.js modules in our front-end, we need a build script. For this, we opted to use webpack because that is probably what people are most familiar with, but you can use your favorite bundler.

Let's write our configuration file:

module.exports = {
 entry: {
    app: './src/app.js',
    worker: './src/worker.js'
  },
  output: {
    filename: '[name].js',
    path: __dirname + '/app'
  }
}

Lastly, add the build script to package.json:

{
  // ...
  "scripts": {
    "build": "webpack"
  }
  // ...
}

You can now build the front-end of your app either by running npm run build or aragon build, whichever you prefer.

Note that aragon build will fail for now, since we have not specified a compiler for our contracts (we will do this in a minute).

Sending Notifications

As a little extra thing, let's also build a super spammy app that will send a notification each time the counter is incremented or decremented.

Preferably, we should do this in the background worker, so let's modify it a tiny bit:

// ...
const state$ = app.store((state, event) => {
  // ...
  switch (event.event) {
    case 'Decrement':
      state--
      app.notify('Counter decremented', `The counter was decremented to ${state}`)
      break
    case 'Increment':
      state++
      app.notify('Counter incremented', `The counter was incremented to ${state}`)
      break
  }
  // ...
})

Simple!

Writing The Manifest Files

In order for Aragon.js to function, it needs some metadata about your app. This metadata is specified in two manifest files; manifest.json and arapp.json.

arapp.json defines smart contract and APM-specific things, like the roles in your app and the name and version of your app.

Let's modify arapp.json so that it knows about the roles we defined previously:

// arapp.json
{
  "appName": "foo.aragonpm.eth",
  "version": "1.0.0",
  "roles": [
      { "name": "Increment the counter", "id": "INCREMENT_ROLE", "params": [] },
      { "name": "Decrement the counter", "id": "DECREMENT_ROLE", "params": [] }
  ],
  "path": "contracts/Counter.sol"
}

manifest.json defines end-user specific things, like the human-readable name of your app, icons and a small description of your app. It also (optionally) defines background scripts, of which we have one.

Let's modify it accordingly:

{
  "name": "Counter",
  "description": "My first Aragon app",
  "script": "worker.js"
}

Note that the script key is relative to the published directory, which in our case is app.

Running Your App Locally

To test out your app without deploying a DAO yourself, installing apps, setting up permissions and setting up APM, you can simply run:

aragon run

This will do a couple of things for you:

  • It will start a development chain you can interact with (it uses ganache-core, so it's a full testrpc instance)
  • It deploys an Aragon DAO with apps and development permissions (i.e. everyone can do everything)
  • It publishes your app to a local APM instance
  • It installs your app

After running this command, you can open up the Aragon dapp and update your DAO address and Ethereum node fields to point to your local machine.

Publishing

Now that we're confident that our app will work and amaze the world, we should publish it.

To publish it, simply run:

aragon publish

This will give you a transaction to sign that will either register the repository (if it does not exist) or publish a new version (if the repository exists). Furthermore, it will run your build script (if available) and publish your front-end and manifest files to IPFS.

Now you just need to share the great news on Twitter and Reddit, to let people know that you've built something great!

Next Steps

The full source code of the application we've built in this guide is available on our GitHub.

A good place to go from here would be to check out our existing apps. They're fairly self-contained and use some patterns you might find helpful.

There's much more to aragonOS 3 and Aragon.js, and we even have our own UI toolkit. We encourage you to explore all 3 and provide us feedback.

Join the conversation and ask questions on GitHub and Aragon Chat, and make sure to tell us if you build something ara-mazing!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.