Dappsys

Safe, simple, flexible building-blocks for smart-contract systems.


Auth
ds-auth ds-guard ds-roles
Token
ds-token ds-vault
Oracle
ds-cache ds-value
Util
ds-exec ds-math ds-note ds-proxy ds-stop ds-thing
Gov
ds-multisig

DSGroup

Multisig with a command-line interface

The ds-group library is DSGroup with a command-line interface. A list of members, the required quorum and the window of time in which actions must be approved are fixed when the DSGroup contract is created. Actions can then be proposed, confirmed and triggered once a group quorum has been reached.

Installation & deployment

The DSGroup contract takes three parameters:

function DSGroup(address[] members, uint quorum, uint window)

address[] members

The list of group members. They will be able to create new proposals, accept them and trigger their execution.

uint quorum

The minimum number of members who have to accept a proposal before it can be triggered.

uint window

The proposal validity time in seconds.

Install Dapp to build and deploy the contract:

dapp build
dapp create DSGroup '[
  0011111111111111111111111111111111111111,
  0022222222222222222222222222222222222222,
  0033333333333333333333333333333333333333
]' 2 86400

Install the Seth dependency in order to use the command line interface. Then type make link from the ds-group directory to install the ds-group CLI tool:

Usage: ds-group <command> <group> [<args>]
   or: ds-group <command> --help

Commands:

action        print information about a multisig action
confirm       confirm a proposed multisig action
ls            list already-proposed multisig actions
propose       propose a new multisig action
trigger       trigger a confirmed multisig action
verify        verify the meaning of a multisig action

Examples

~$ ds-group ls @mkrgroup
    ACT  CONFIRMATIONS      EXPIRATION  STATUS
     15   0/6 (need 4)        8 h left  Unconfirmed
     16   0/6 (need 4)        9 h left  Unconfirmed

~$ ds-group propose @mkrgroup @feedbase 0 "claim()"
Proposing action...
  target     0x5927c5cc723c4486f93bf90bad3be8831139499e
  value      0
  calldata   0x4e71d92d
seth-send: 0x307b667c434794c234b7c463b26827bdceb9c838fdb306f3f4398edefa5b1310
seth-send: Waiting for transaction receipt.........................
seth-send: Transaction included in block 1519991.
seth-send: note: return value may be inaccurate (see `seth send --help')
Successfully proposed act 17.

~$ ds-group ls @mkrgroup
    ACT  CONFIRMATIONS      EXPIRATION  STATUS
     15   0/6 (need 4)        8 h left  Unconfirmed
     16   0/6 (need 4)        9 h left  Unconfirmed
     17   0/6 (need 4)       23 h left  Unconfirmed

~$ ds-group confirm @mkrgroup 17
Confirming action 17...
seth-send: 0x72fc6bf7c5135645a0fa298aa3ae01e072a82eabfddc8e3fbcdca72d0007d94b
seth-send: Waiting for transaction receipt...............
seth-send: Transaction included in block 1520018.

~$ ds-group ls @mkrgroup
 ACTION  CONFIRMATIONS      EXPIRATION  STATUS
     15   0/6 (need 4)        8 h left  Unconfirmed
     16   0/6 (need 4)        9 h left  Unconfirmed
     17   1/6 (need 4)       23 h left  Unconfirmed

~$ ds-group trigger @mkrgroup 17
ds-group-trigger: error: act not confirmed: 17

~$ ds-group action @mkrgroup 17
calldata        0x4e71d92d
confirmations   1
confirmed       false
deadline        1471876934
expired         false
status          Unconfirmed
target          0x5927c5cc723c4486f93bf90bad3be8831139499e
triggered       false
value           0

DSGroup

  /// group.sol -- simple m-of-n multisig implementation

// Copyright (C) 2015, 2016  Ryan Casey <[email protected]>
// Copyright (C) 2016, 2017  Daniel Brockman <[email protected]>

// Licensed under the Apache License, Version 2.0 (the "License").
// You may not use this file except in compliance with the License.

// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND (express or implied).

pragma solidity ^0.4.11;

import "ds-exec/exec.sol";
import "ds-note/note.sol";

contract DSGroup is DSExec, DSNote {
    address[]  public  members;
    uint       public  quorum;
    uint       public  window;
    uint       public  actionCount;

    mapping (uint => Action)                     public  actions;
    mapping (uint => mapping (address => bool))  public  confirmedBy;
    mapping (address => bool)                    public  isMember;

    // Legacy events
    event Proposed   (uint id, bytes calldata);
    event Confirmed  (uint id, address member);
    event Triggered  (uint id);

    struct Action {
        address  target;
        bytes    calldata;
        uint     value;

        uint     confirmations;
        uint     deadline;
        bool     triggered;
    }

    function DSGroup(
        address[]  members_,
        uint       quorum_,
        uint       window_
    ) {
        members  = members_;
        quorum   = quorum_;
        window   = window_;

        for (uint i = 0; i < members.length; i++) {
            isMember[members[i]] = true;
        }
    }

    function memberCount() constant returns (uint) {
        return members.length;
    }

    function target(uint id) constant returns (address) {
        return actions[id].target;
    }
    function calldata(uint id) constant returns (bytes) {
        return actions[id].calldata;
    }
    function value(uint id) constant returns (uint) {
        return actions[id].value;
    }

    function confirmations(uint id) constant returns (uint) {
        return actions[id].confirmations;
    }
    function deadline(uint id) constant returns (uint) {
        return actions[id].deadline;
    }
    function triggered(uint id) constant returns (bool) {
        return actions[id].triggered;
    }

    function confirmed(uint id) constant returns (bool) {
        return confirmations(id) >= quorum;
    }
    function expired(uint id) constant returns (bool) {
        return now > deadline(id);
    }

    function deposit() note payable {
    }

    function propose(
        address  target,
        bytes    calldata,
        uint     value
    ) onlyMembers note returns (uint id) {
        id = ++actionCount;

        actions[id].target    = target;
        actions[id].calldata  = calldata;
        actions[id].value     = value;
        actions[id].deadline  = now + window;

        Proposed(id, calldata);
    }

    function confirm(uint id) onlyMembers onlyActive(id) note {
        assert(!confirmedBy[id][msg.sender]);

        confirmedBy[id][msg.sender] = true;
        actions[id].confirmations++;

        Confirmed(id, msg.sender);
    }

    function trigger(uint id) onlyMembers onlyActive(id) note {
        assert(confirmed(id));

        actions[id].triggered = true;
        exec(actions[id].target, actions[id].calldata, actions[id].value);

        Triggered(id);
    }

    modifier onlyMembers {
        assert(isMember[msg.sender]);
        _;
    }

    modifier onlyActive(uint id) {
        assert(!expired(id));
        assert(!triggered(id));
        _;
    }

    //------------------------------------------------------------------
    // Legacy functions
    //------------------------------------------------------------------

    function getInfo() constant returns (
        uint  quorum_,
        uint  memberCount,
        uint  window_,
        uint  actionCount_
    ) {
        return (quorum, members.length, window, actionCount);
    }

    function getActionStatus(uint id) constant returns (
        uint     confirmations,
        uint     deadline,
        bool     triggered,
        address  target,
        uint     value
    ) {
        return (
            actions[id].confirmations,
            actions[id].deadline,
            actions[id].triggered,
            actions[id].target,
            actions[id].value
        );
    }
}

contract DSGroupFactory is DSNote {
    mapping (address => bool)  public  isGroup;

    function newGroup(
        address[]  members,
        uint       quorum,
        uint       window
    ) note returns (DSGroup group) {
        group = new DSGroup(members, quorum, window);
        isGroup[group] = true;
    }
}

Tools for dapps

We believe that the free software movement is the most important cultural predecessor to the modern-day renaissance in decentralized technologies.

To catalyze the growth of this ecosystem, and to empower hackers to participate, we’re building a comprehensive suite of blockchain-oriented developer tools in the spirit of the Unix philosophy.

Dapp is all you need to start developing for Ethereum. It creates new dapps, runs Solidity unit tests, debugs, deploys, launches testnets, and more.

Seth is a handy tool for slicing and dicing transactions, querying the blockchain, converting between data formats, performing remote calls, and other everyday tasks.

Hevm is our own EVM implementation with a nimble terminal-based Solidity debugger. It’s used for dapp test and dapp debug.

Evmdis is an EVM disassembler written and maintained by Nick Johnson. It’s useful to make sense of EVM bytecode, especially when developing contracts at the assembly or raw bytecode level.

Dappsys - smart contract building blocks

We also maintain Dappsys, an audited collection of smart contract building blocks designed to complement each other. They include;

Using these proven parts lets us focus on the novel features of the systems we develop. We share Dappsys to benefit the smart contract ecosystem.