Safe, simple, flexible building-blocks for smart-contract systems.
ds-auth
ds-guard
ds-roles
ds-token
ds-vault
ds-cache
ds-value
ds-exec
ds-math
ds-note
ds-proxy
ds-stop
ds-thing
ds-multisig
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.
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
~$ 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
/// 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;
}
}
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
.
We also maintain Dappsys, an audited collection of smart contract building blocks designed to complement each other. They include;
ds-token
—
a generic EIP-20 coin;
ds-group
—
a multisig;
ds-guard
—
a flexible authority rule;
ds-proxy
—
a transaction proxy; and
ds-cache
—
a store of expiring values.
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.