White Paper
White Paper
White Paper
CHRISTOPH JENTZSCH
FOUNDER & CTO, SLOCK.IT
[email protected]
Abstract. This paper describes the first implementation of Decentralized Autonomous Organization (DAO) code to
automate organizational governance and decision-making. The code can be used by individuals working together
collaboratively outside of a traditional corporate form. It can also be used by a registered corporate entity to automate
formal governance rules contained in corporate bylaws or imposed by law. First the DAO concept is described, then
minority rights is discussed, and a solution to a robbing the minority attack vector is proposed. Finally, a practical
implementation of a first generation DAO entity is provided using smart contracts written in Solidity on the Ethereum
blockchain.
technology which integrates a Turing complete programming language with smart contract processing functionality. This paper illustrates a method that for the first
time allows the creation of organizations in which (1) participants maintain direct real-time control of contributed
funds and (2) governance rules are formalized, automated
and enforced using software. Specifically, standard smart
contract code (Szabo [1997], Miller [1997]) has been written that can be used to form a Decentralized Autonomous
Organization (DAO) on the Ethereum blockchain. This
paper explains how a DAOs code works, focusing on some
basic formation and governance features, including structure, creation and voting rights.
First a DAOs Creation Phase and basic functionality
are described. Then minority owner rights are discussed
and a solution to the Majority Robbing the Minority Attack problem is proposed: the DAO split. The smart
contract code is then explored in detail, and conclude with
an explanation and detailed specification of the DAO
split.
The code for the smart contracts is located at: https:
//github.com/slockit/DAO/
A word of caution, at the outset: the legal status of
DAOs remains the subject of active and vigorous debate
and discussion. Not everyone shares the same definition.
Some have said that they are autonomous code and can
operate independently of legal systems; others have said
that they must be owned or operate by humans or human created entities. There will be many uses cases, and
the DAO code will develop over time. Ultimately, how a
DAO functions and its legal status will depend on many
factors, including how DAO code is used, where it is used,
and who uses it. This paper does not speculate about
the legal status of DAOs worldwide. This paper is not
intended to offer legal advice or conclusions. Anyone who
uses DAO code will do so at their own risk.
1. Introduction
Corporate entities of all kinds are governed by rules
that describe permitted and proscribed conduct. These
rules may exist as private contracts (like bylaws or shareholder agreements) between corporate owners. They may
also be imposed by law in addition to or in the absence of
a written agreement between participants.
Historically, corporations have only been able to act
through people (or through corporate entities that were
themselves ultimately controlled by people). This presents
two simple and fundamental problems. Whatever a private contract or public law require: (1) people do not
always follow the rules and (2) people do not always agree
what the rules actually require. Collaboration without a
corporate form does not solve these problems, necessarily,
and it may introduce others. In the absence of a corporate
form, an explicit written agreement is substituted for unclear informal understandings and the legal protections
provided by a corporate form will not be available.
Rule-breaking within an organization not always obvious, and motives may not matter to stakeholders once
their money has been lost. While bad behavior may make
a corporation or its management civilly or criminally liable, punishment can come as little comfort to an investor
who has already lost their money.
Crowdfunding (Massolution [2015]) amplifies the problem. On the one hand, it has made it easier for small contributors to invest in large projects, and it has also made it
possible for entrepreneurs to receive financial support that
might not have been easily available in the past. On the
other hand, small investors remain vulnerable to financial
mismanagement or outright fraud, and because they have
a small stake in a venture, they may lack power to identify
problems, participate in governance decisions, or to easily recover their investment (Knibbs [2015], Biggs [2015]).
At the same time, corporate leadership and management
may be accused of malfeasance or mismanagement when
they believe that they have acted in good faith, based on
their understanding of their obligations and interpretation
of applicable rules.
This paper presents a potential solution using
Ethereum, (Buterin [2013], Wood [2014]) a blockchain
2. Dao Concept
DAO code is written in the Solidity programming
language. A DAO is activated by deployment on the
Ethereum blockchain.
Once deployed, a DAOs code requires ether to engage in transactions on Ethereum. Ether is the digital
1
qmin =
Ttotal
transfer Ttotal
+
d
3 (DAO + RDAO )
Where d is the minQuorumDivisor. This parameters default value is 5, but it will double in the case the quorum
has not been met for over a year. DAO is the amount
of ether owned by a DAO and RDAO is the amount of reward tokens owned by this DAO, as explained in section 7
(also see rewardToken in A.3). The sum DAO + RDAO is
equal to the amount of ether used to Create DAO tokens
this power, it is possible for a DAO to vote for a new Curator, which may result in a split of the DAO as described
above.
Any token holder can make a proposal to vote for a
new Curator. In effect, even a single token holder is able
to both retrieve their remaining portion of ether and maintain their right to any future rewards associated to their
previous contribution, as these will be sent to the new
DAO automatically. Rewards are defined as any ether
received by a DAO generated from products the DAO
funded so far and are explained in further detail in section
7.
The process of choosing a new Curator is as follows:
Any token holder can submit a proposal for a new Curator. In this case, no proposal deposit is required, because an attacker could vote for an extremely high deposit, preventing any splits. The debating period for this
proposal is 7 days. This is 7 days less than the minimum
required for regular proposals, allowing anyone to retrieve
their funds before a potentially malicious proposal goes
through. There is no quorum requirement, so that every
token holder has the ability to split into their own DAO.
The debating period is used to discuss (on or off-chain) the
new Curator and conduct a first vote thats non-binding.
After this first vote, token holders can confirm its results
or not. In the case a majority opts to keep the original
Curator, the minority can either keep the original Curator in order to avoid a split, or inversely they can confirm
their vote for a new Curator and move their portion of the
ether to a new DAO.
5. Token Price
DAO Token Creation rate decreases over time. This reflects an assumption that early acts of DAO Creation have
greater risk, as they may have less information about the
potential success of the DAO and do not know whether
what contribution will lead to full fueling of the DAO. The
DAO described in this paper has the following Creation
schedule:
(2)
1
if t < tc 2 w
1.5
otherwise
with the multiplier m defined as:
(3)
6.1. Token.
contract TokenInterface {
mapping (address => uint256) balances;
mapping (address => mapping (address => uint256)) allowed;
uint256 public totalSupply;
function balanceOf(address _owner) constant returns (uint256 balance);
function transfer(address _to, uint256 _amount) returns (bool success);
function transferFrom(address _from, address _to, uint256 _amount) returns (bool success);
function approve(address _spender, uint256 _amount) returns (bool success);
function allowance(address _owner, address _spender) constant returns (uint256 remaining);
event Transfer(address indexed _from, address indexed _to, uint256 _amount);
event Approval(address indexed _owner, address indexed _spender, uint256 _amount);
}
6.2. TokenCreation.
contract TokenCreationInterface {
uint public closingTime;
uint public minTokensToCreate;
bool public isFueled;
address public privateCreation;
ManagedAccount extraBalance;
mapping (address => uint256) weiGiven;
function
function
function
function
6.3. DAO.
contract DAOInterface {
Proposal[] public proposals;
uint minQuorumDivisor;
uint lastTimeMinQuorumMet;
address public curator;
address[] public allowedRecipients;
mapping (address => uint) public rewardToken;
uint public totalRewardToken;
ManagedAccount public rewardAccount;
ManagedAccount public DAOrewardAccount;
mapping (address => uint) public paidOut;
mapping (address => uint) public DAOpaidOut;
mapping (address => uint) public blocked;
uint public proposalDeposit;
uint sumOfProposalDeposits;
DAO_Creator public daoCreator;
struct Proposal {
address recipient;
uint amount;
string description;
uint votingDeadline;
bool open;
bool proposalPassed;
bytes32 proposalHash;
uint proposalDeposit;
bool newCurator;
SplitData[] splitData;
uint yea;
uint nay;
mapping (address => bool) votedYes;
mapping (address => bool) votedNo;
address creator;
}
struct SplitData {
uint splitBalance;
uint totalSupply;
uint rewardToken;
DAO newDAO;
}
modifier onlyTokenholders {}
function DAO(
address _curator,
DAO_Creator _daoCreator,
uint _proposalDeposit,
uint _minTokensToCreate,
uint _closingTime,
address _privateCreation
)
function () returns (bool success);
function receiveEther() returns(bool);
function newProposal(
address _recipient,
uint _amount,
string _description,
bytes _transactionData,
uint _debatingPeriod,
bool __newCurator
) onlyTokenholders returns (uint _proposalID);
function checkProposalCode(
uint _proposalID,
address _recipient,
uint _amount,
bytes _transactionData
) constant returns (bool _codeChecksOut);
function vote(
uint _proposalID,
bool _supportsProposal
) onlyTokenholders returns (uint _voteID);
function executeProposal(
uint _proposalID,
bytes _transactionData
) returns (bool _success);
function splitDAO(
uint _proposalID,
address _newCurator
) returns (bool _success);
function newContract(address _newContract);
function changeAllowedRecipients(address _recipient, bool _allowed) external returns (bool _success);
function changeProposalDeposit(uint _proposalDeposit) external;
function retrieveDAOReward(bool _toMembers) external returns (bool _success);
function getMyReward() returns(bool _success);
function withdrawRewardFor(address _account) returns(bool _success);
function transferWithoutReward(address _to, uint256 _amount) returns (bool success);
function transferFromWithoutReward(
address _from,
address _to,
uint256 _amount
) returns (bool success);
function halveMinQuorum() returns (bool _success);
function numberOfProposals() constant returns (uint _numberOfProposals);
function getNewDAOAdress(uint _proposalID) constant returns (address _newDAO);
function isBlocked(address _account) internal returns (bool);
function unblockMe() returns (bool);
event ProposalAdded(
uint indexed proposalID,
address recipient,
uint amount,
bool newCurator,
string description
);
event Voted(uint indexed proposalID, bool position, address indexed voter);
event ProposalTallied(uint indexed proposalID, bool result, uint quorum);
event NewCurator(address indexed _newCurator);
event AllowedRecipientAdded(address indexed _recipient);
}
will double in the case a quorum has not been reached for
over a year.
The integer lastTimeMinQuorumMet keeps track of the
last time the quorum was reached.
The address curator is set at the creation of the DAO
and defines the Curator.
The list allowedRecipients is commonly referred to
as the whitelist. The DAO can only send transactions to
itself, the curator, extraBalance and addresses in the
whitelist. Only the curator can add/remove addresses
to/from the whitelist.
The map rewardToken tracks the addresses that are
owed rewards generated by the products of the DAO.
Those addresses can only be DAOs.
The integer totalRewardToken tracks the amount of
reward tokens in existence.
The variable rewardAccount is of the type
ManagedAccount , which will be discussed in A.5. It
is used to manage the rewards which are to be distributed
to the DAO Token Holders.
Similar, DAOrewardAccount is also of the type
ManagedAccount. This account is used to receive all rewards from projects funded by the DAO. It will then redistribute them amongst all splitted DAOs as well as itself
using the function retrieveDAOReward.
The map paidOut is used to keep track how much
wei a single token holder has already retrieved from
rewardAccount.
Similar, the map DAOpaidOut is used to keep track
how much a single DAO has already received from
DAOrewardAccount.
The map blocked stores the addresses of the DAO Tokens that have voted and therefore are not allowed to be
transferred until the vote has concluded. The address
points to the proposal ID.
The integer proposalDeposit specifies the minimum
deposit to be paid in wei for any proposal that does not
include a change of the Curator.
The integer sumOfProposalDeposits is the sum of all
proposal deposits of open proposals.
The contract daoCreator is used to create a new DAO
with the same code as this DAO, used in the case of a
split.
A single proposal has the parameters:
recipient: The address where the amount of wei will
go to if the proposal is accepted.
amount: The amount of wei to transfer to
recipient if the proposal is accepted.
description: A plain text description of the proposal.
votingDeadline: A unix timestamp, denoting the
end of the voting period.
open: A boolean which is false if the votes have already been counted, true otherwise.
proposalPassed: A boolean which is true if a quorum has been achieved with the majority approving the proposal.
proposalHash: A
hash
to
check
validity
of
a
proposal.
Equal
to
sha3(_recipient, _amount, _transactionData).
proposalDeposit: The deposit (in wei) the creator
of a proposal has send to submit a proposal. It is
taken from the msg.value of a newProposal call;
7. Reward Tokens
This section gives a description of how reward tokens
are implemented in this contract. Much of the information has already been explained, but it is restated here for
clarity.
Ttotal =
2256
X1
Ti
i=0
Where Ti is the amount of DAO tokens owned by an address i (balances[i]). Note that 2256 is the total number
of possible addresses in Ethereum. Similarly, the amount
of reward tokens Rtotal is defined as follows:
(9)
numP roposals
2256
X1
X
Rtotal =
Ri =
p.amount
i=0
p=0;p.proposalP assed=true
10
9. Updates
Although the code of the contract specified at a certain
address in the Ethereum blockchain can not be changed,
there might still be a need for a single member or the DAO
as a whole to change the contracts. Every single member
can always split the DAO as described above and move
their funds to a new DAO. From there they can move their
funds to another new DAO with a new smart contract.
But in order to use a new code for the complete DAO one
can simply create a new DAO contract with all the needed
features and deploy it on the blockchain, and make a proposal to call the newContract function with the address
of the new contract as parameter. If accepted, the complete DAO moves to the new contract, meaning, all ether
and reward tokens are transferred to the new contract.
In order to use the same underlying DAO tokens there,
one can use the approve function and give the new DAO
the right to move the tokens. In the new contract this
right should only be usable in restricted functions which
are only callable by the owner of the tokens. Another option is to create new tokens in the new contract based on
the token distribution in the old contract. This can also
be achieved by a proof that the old tokens are destroyed
(sending to the 0 address). This process allows for the
DAO to maintain static immutable code on the Ethereum
blockchain, while still being able to be updated if the need
arises.
10. Acknowledgements
I want to thank Stephan Tual and Simon Jentzsch for
fruitful discussions and corrections, as well as Gavin Wood
and Christian Reitwiessner for a review of the contracts
and the development of Solidity, the programing language
used to write the contracts.
Special thanks goes to Yoichi Hirai and Lefteris Karapetsas for reviewing the smart contracts and making significant improvements.
I also want to thank Griff Green for reviewing and editing the paper.
Last but not least I want to thank our community
which has given feedback, corrections and encouragement.
11
References
John Biggs. When Crowdfunding Fails The Backers Are Left With No Way Out. 2015. URL http://techcrunch.com/
2015/11/19/when-crowdfunding-fails-the-backers-are-left-with-no-way-out/.
Vitalik Buterin. Ethereum: A Next-Generation Smart Contract and Decentralized Application Platform. 2013. URL
https://github.com/ethereum/wiki/wiki/White-Paper.
Vitalik Buterin. The Subjectivity / Exploitability Tradeoff. 2015. URL https://blog.ethereum.org/2015/02/14/
subjectivity-exploitability-tradeoff/.
Griff Green. private discussion. 2016.
Kate Knibbs.
The 9 Most Disgraceful Crowdfunding Failures of 2015.
2015.
URL http://gizmodo.com/
the-9-most-disgraceful-crowdfunding-failures-of-2015-1747957776.
Massolution. 2015CF - Crowdfunding Industry Report. 2015. URL http://reports.crowdsourcing.org/index.php?
route=product/product&path=0_20&product_id=54.
Mark Miller. The Future of Law. In paper delivered at the Extro 3 Conference (August 9), 1997.
Christian Reitwiessner and Gavin Wood. Solidity. 2015. URL http://solidity.readthedocs.org/.
Nick Szabo. Formalizing and securing relationships on public networks. First Monday, 2(9), 1997.
Gavin Wood. Ethereum: A Secure Decentralised Generalised Transaction Ledger. 2014. URL http://gavwood.com/
paper.pdf.
Appendix A. Contracts
A.1. Token.
contract TokenInterface {
mapping (address => uint256) balances;
mapping (address => mapping (address => uint256)) allowed;
/// @return Total amount of tokens
uint256 public totalSupply;
/// @param _owner The address from which the balance will be retrieved
/// @return The balance
function balanceOf(address _owner) constant returns (uint256 balance);
/// @notice Send _amount tokens to _to from msg.sender
/// @param _to The address of the recipient
/// @param _amount The amount of tokens to be transferred
/// @return Whether the transfer was successful or not
function transfer(address _to, uint256 _amount) returns (bool success);
/// @notice Send _amount tokens to _to from _from on the condition it
/// is approved by _from
/// @param _from The address of the sender
/// @param _to The address of the recipient
/// @param _amount The amount of tokens to be transferred
/// @return Whether the transfer was successful or not
function transferFrom(address _from, address _to, uint256 _amount)
returns (bool success);
/// @notice msg.sender approves _spender to spend _amount tokens on
/// its behalf
/// @param _spender The address of the account able to transfer the tokens
/// @param _amount The amount of tokens to be approved for transfer
/// @return Whether the approval was successful or not
function approve(address _spender, uint256 _amount) returns (bool success);
/// @param _owner The address of the account owning tokens
/// @param _spender The address of the account able to transfer the tokens
/// @return Amount of remaining tokens of _owner that _spender is allowed
/// to spend
function allowance(address _owner, address _spender)
constant
returns (uint256 remaining);
event Transfer(address indexed _from, address indexed _to, uint256 _amount);
event Approval(
address indexed _owner,
address indexed _spender,
uint256 _amount
);
}
12
A.2. TokenCreation.
contract TokenCreationInterface {
// End of token creation, in Unix time
uint public closingTime;
// Minimum fueling goal of the token creation, denominated in tokens to
// be created
uint public minTokensToCreate;
// True if the DAO reached its minimum fueling goal, false otherwise
bool public isFueled;
// For DAO splits - if privateCreation is 0, then it is a public token
// creation, otherwise only the address stored in privateCreation is
// allowed to create tokens
address public privateCreation;
// hold extra ether which has been sent after the DAO token
// creation rate has increased
ManagedAccount public extraBalance;
// tracks the amount of wei given from each contributor (used for refund)
mapping (address => uint256) weiGiven;
/// @dev Constructor setting the minimum fueling goal and the
/// end of the Token Creation
/// @param _minTokensToCreate Minimum required wei-equivalent tokens
///
to be created for a successful DAO Token Creation
/// @param _closingTime Date (in Unix time) of the end of the Token Creation
/// @param _privateCreation Zero means that the creation is public. A
/// non-zero address represents the only address that can create Tokens
/// (the address can also create Tokens on behalf of other accounts)
// This is the constructor: it can not be overloaded so it is commented out
// function TokenCreation(
// uint _minTokensTocreate,
// uint _closingTime,
// address _privateCreation
// );
/// @notice Create Token with _tokenHolder as the initial owner of the Token
/// @param _tokenHolder The address of the Tokenss recipient
/// @return Whether the token creation was successful
function createTokenProxy(address _tokenHolder) returns (bool success);
/// @notice Refund msg.sender in the case the Token Creation did
/// not reach its minimum fueling goal
function refund();
/// @return The divisor used to calculate the token creation rate during
/// the creation phase
function divisor() returns (uint divisor);
event FuelingToDate(uint value);
event CreatedToken(address indexed to, uint amount);
event Refund(address indexed to, uint value);
}
13
14
15
struct Proposal {
// The address where the amount will go to if the proposal is accepted
// or if newCurator is true, the proposed Curator of
// the new DAO).
address recipient;
// The amount to transfer to recipient if the proposal is accepted.
uint amount;
// A plain text description of the proposal
string description;
// A unix timestamp, denoting the end of the voting period
uint votingDeadline;
// True if the proposals votes have yet to be counted, otherwise False
bool open;
// True if quorum has been reached, the votes have been counted, and
// the majority said yes
bool proposalPassed;
// A hash to check validity of a proposal
bytes32 proposalHash;
// Deposit in wei the creator added when submitting their proposal. It
// is taken from the msg.value of a newProposal call.
uint proposalDeposit;
// True if this proposal is to assign a new Curator
bool newCurator;
// Data needed for splitting the DAO
SplitData[] splitData;
// Number of Tokens in favor of the proposal
uint yea;
// Number of Tokens opposed to the proposal
uint nay;
// Simple mapping to check if a shareholder has voted for it
mapping (address => bool) votedYes;
// Simple mapping to check if a shareholder has voted against it
mapping (address => bool) votedNo;
// Address of the shareholder who created the proposal
address creator;
}
// Used only in the case of a newCurator proposal.
struct SplitData {
// The balance of the current DAO minus the deposit at the time of split
uint splitBalance;
// The total amount of DAO Tokens in existence at the time of split.
uint totalSupply;
// Amount of Reward Tokens owned by the DAO at the time of split.
uint rewardToken;
// The new DAO contract created at the time of split.
DAO newDAO;
}
// Used to restrict access to certain functions to only DAO Token Holders
modifier onlyTokenholders {}
///
///
///
///
///
///
///
///
///
///
///
16
17
18
uint _proposalID,
bool _supportsProposal
) onlyTokenholders returns (uint _voteID);
/// @notice Checks whether proposal _proposalID with transaction data
/// _transactionData has been voted for or rejected, and executes the
/// transaction in the case it has been voted for.
/// @param _proposalID The proposal ID
/// @param _transactionData The data of the proposed transaction
/// @return Whether the proposed transaction has been executed or not
function executeProposal(
uint _proposalID,
bytes _transactionData
) returns (bool _success);
/// @notice ATTENTION! I confirm to move my remaining ether to a new DAO
/// with _newCurator as the new Curator, as has been
/// proposed in proposal _proposalID. This will burn my tokens. This can
/// not be undone and will split the DAO into two DAOs, with two
/// different underlying tokens.
/// @param _proposalID The proposal ID
/// @param _newCurator The new Curator of the new DAO
/// @dev This function, when called for the first time for this proposal,
/// will create a new DAO and send the senders portion of the remaining
/// ether and Reward Tokens to the new DAO. It will also burn the DAO Tokens
/// of the sender.
function splitDAO(
uint _proposalID,
address _newCurator
) returns (bool _success);
/// @dev can only be called by the DAO itself through a proposal
/// updates the contract of the DAO by sending all ether and rewardTokens
/// to the new DAO. The new DAO needs to be approved by the Curator
/// @param _newContract the address of the new contract
function newContract(address _newContract);
19
_
}
function DAO(
address _curator,
DAO_Creator _daoCreator,
uint _proposalDeposit,
uint _minTokensToCreate,
uint _closingTime,
address _privateCreation
) TokenCreation(_minTokensToCreate, _closingTime, _privateCreation) {
curator = _curator;
daoCreator = _daoCreator;
proposalDeposit = _proposalDeposit;
rewardAccount = new ManagedAccount(address(this), false);
DAOrewardAccount = new ManagedAccount(address(this), false);
if (address(rewardAccount) == 0)
throw;
if (address(DAOrewardAccount) == 0)
throw;
lastTimeMinQuorumMet = now;
minQuorumDivisor = 5; // sets the minimal quorum to 20%
proposals.length = 1; // avoids a proposal with ID 0 because it is used
allowedRecipients[address(this)] = true;
allowedRecipients[curator] = true;
}
function () returns (bool success) {
if (now < closingTime + creationGracePeriod && msg.sender != address(extraBalance))
return createTokenProxy(msg.sender);
else
return receiveEther();
}
function newProposal(
address _recipient,
uint _amount,
string _description,
bytes _transactionData,
uint _debatingPeriod,
bool _newCurator
) onlyTokenholders returns (uint _proposalID) {
// Sanity check
if (_newCurator && (
_amount != 0
|| _transactionData.length != 0
|| _recipient == curator
|| msg.value > 0
|| _debatingPeriod < minSplitDebatePeriod)) {
throw;
} else if (
!_newCurator
&& (!isRecipientAllowed(_recipient) || (_debatingPeriod <
) {
minProposalDebatePeriod))
20
throw;
}
if (_debatingPeriod > 8 weeks)
throw;
if (!isFueled
|| now < closingTime
|| (msg.value < proposalDeposit && !_newCurator)) {
throw;
}
if (now + _debatingPeriod < now) // prevents overflow
throw;
// to prevent a 51% attacker to convert the ether into deposit
if (msg.sender == address(this))
throw;
_proposalID = proposals.length++;
Proposal p = proposals[_proposalID];
p.recipient = _recipient;
p.amount = _amount;
p.description = _description;
p.proposalHash = sha3(_recipient, _amount, _transactionData);
p.votingDeadline = now + _debatingPeriod;
p.open = true;
//p.proposalPassed = False; // thats default
p.newCurator = _newCurator;
if (_newCurator)
p.splitData.length++;
p.creator = msg.sender;
p.proposalDeposit = msg.value;
sumOfProposalDeposits += msg.value;
ProposalAdded(
_proposalID,
_recipient,
_amount,
_newCurator,
_description
);
}
function checkProposalCode(
uint _proposalID,
address _recipient,
uint _amount,
bytes _transactionData
) noEther constant returns (bool _codeChecksOut) {
Proposal p = proposals[_proposalID];
return p.proposalHash == sha3(_recipient, _amount, _transactionData);
}
function vote(
uint _proposalID,
bool _supportsProposal
) onlyTokenholders noEther returns (uint _voteID) {
21
Proposal p = proposals[_proposalID];
if (p.votedYes[msg.sender]
|| p.votedNo[msg.sender]
|| now >= p.votingDeadline) {
throw;
}
if (_supportsProposal) {
p.yea += balances[msg.sender];
p.votedYes[msg.sender] = true;
} else {
p.nay += balances[msg.sender];
p.votedNo[msg.sender] = true;
}
if (blocked[msg.sender] == 0) {
blocked[msg.sender] = _proposalID;
} else if (p.votingDeadline > proposals[blocked[msg.sender]].votingDeadline) {
// this proposals voting deadline is further into the future than
// the proposal that blocks the sender so make it the blocker
blocked[msg.sender] = _proposalID;
}
Voted(_proposalID, _supportsProposal, msg.sender);
}
function executeProposal(
uint _proposalID,
bytes _transactionData
) noEther returns (bool _success) {
Proposal p = proposals[_proposalID];
uint waitPeriod = p.newCurator
? splitExecutionPeriod
: executeProposalPeriod;
// If we are over deadline and waiting period, assert proposal is closed
if (p.open && now > p.votingDeadline + waitPeriod) {
closeProposal(_proposalID);
return;
}
// Check if the proposal can be executed
if (now < p.votingDeadline // has the voting deadline arrived?
// Have the votes been counted?
|| !p.open
// Does the transaction code match the proposal?
|| p.proposalHash != sha3(p.recipient, p.amount, _transactionData)) {
throw;
}
// If the curator removed the recipient from the whitelist, close the proposal
// in order to free the deposit and allow unblocking of voters
if (!isRecipientAllowed(p.recipient)) {
closeProposal(_proposalID);
p.creator.send(p.proposalDeposit);
return;
}
bool proposalCheck = true;
22
23
Proposal p = proposals[_proposalID];
// Sanity check
if (now < p.votingDeadline // has the voting deadline arrived?
//The request for a split expires XX days after the voting deadline
|| now > p.votingDeadline + splitExecutionPeriod
// Does the new Curator address match?
|| p.recipient != _newCurator
// Is it a new curator proposal?
|| !p.newCurator
// Have you voted for this split?
|| !p.votedYes[msg.sender]
// Did you already vote on another proposal?
|| (blocked[msg.sender] != _proposalID && blocked[msg.sender] != 0) )
throw;
}
// If the new DAO doesnt exist yet, create the new DAO and store the
// current split data
if (address(p.splitData[0].newDAO) == 0) {
p.splitData[0].newDAO = createNewDAO(_newCurator);
// Call depth limit reached, etc.
if (address(p.splitData[0].newDAO) == 0)
throw;
// should never happen
if (this.balance < sumOfProposalDeposits)
throw;
p.splitData[0].splitBalance = actualBalance();
p.splitData[0].rewardToken = rewardToken[address(this)];
p.splitData[0].totalSupply = totalSupply;
p.proposalPassed = true;
}
// Move ether and assign new Tokens
uint fundsToBeMoved =
(balances[msg.sender] * p.splitData[0].splitBalance) /
p.splitData[0].totalSupply;
if (p.splitData[0].newDAO.createTokenProxy.value(fundsToBeMoved)(msg.sender) == false)
throw;
24
25
Transfer(msg.sender, 0, balances[msg.sender]);
withdrawRewardFor(msg.sender); // be nice, and get his rewards
totalSupply -= balances[msg.sender];
balances[msg.sender] = 0;
paidOut[msg.sender] = 0;
return true;
}
function newContract(address _newContract){
if (msg.sender != address(this) || !allowedRecipients[_newContract]) return;
// move all ether
if (!_newContract.call.value(address(this).balance)()) {
throw;
}
//move all reward tokens
rewardToken[_newContract] += rewardToken[address(this)];
rewardToken[address(this)] = 0;
DAOpaidOut[_newContract] += DAOpaidOut[address(this)];
DAOpaidOut[address(this)] = 0;
}
function transferFrom(address _from, address _to, uint256 _value) returns (bool success) {
if (isFueled
&& now > closingTime
&& !isBlocked(_from)
&& transferPaidOut(_from, _to, _value)
&& super.transferFrom(_from, _to, _value)) {
return true;
} else {
throw;
}
}
function transferFromWithoutReward(
address _from,
address _to,
uint256 _value
) returns (bool success) {
if (!withdrawRewardFor(_from))
throw;
return transferFrom(_from, _to, _value);
}
function transferPaidOut(
address _from,
address _to,
uint256 _value
) internal returns (bool success) {
uint transferPaidOut = paidOut[_from] * _value / balanceOf(_from);
if (transferPaidOut > paidOut[_from])
throw;
paidOut[_from] -= transferPaidOut;
paidOut[_to] += transferPaidOut;
return true;
}
26
27
function changeAllowedRecipients(address _recipient, bool _allowed) noEther external returns (bool _success) {
if (msg.sender != curator)
throw;
allowedRecipients[_recipient] = _allowed;
AllowedRecipientChanged(_recipient, _allowed);
return true;
}
address contractor;
bytes32 hashOfTheTerms;
uint minDailyCosts;
uint paidOut;
uint dateOfSignature;
DAO client; // address of DAO
bool public promiseValid;
uint public rewardDivisor;
uint public deploymentReward;
modifier callingRestriction {
if (promiseValid) {
28
if (msg.sender != address(client))
throw;
} else if (msg.sender != contractor) {
throw;
}
_
}
modifier onlyClient {
if (msg.sender != address(client))
throw;
_
}
function SampleOffer(
address _contractor,
bytes32 _hashOfTheTerms,
uint _totalCosts,
uint _oneTimeCosts,
uint _minDailyCosts,
uint _rewardDivisor,
uint _deploymentReward
) {
contractor = _contractor;
hashOfTheTerms = _hashOfTheTerms;
totalCosts = _totalCosts;
oneTimeCosts = _oneTimeCosts;
minDailyCosts = _minDailyCosts;
dailyCosts = _minDailyCosts;
rewardDivisor = _rewardDivisor;
deploymentReward = _deploymentReward;
}
function sign() {
if (msg.value < totalCosts || dateOfSignature != 0)
throw;
if (!contractor.send(oneTimeCosts))
throw;
client = DAO(msg.sender);
dateOfSignature = now;
promiseValid = true;
}
function setDailyCosts(uint _dailyCosts) onlyClient {
dailyCosts = _dailyCosts;
if (dailyCosts < minDailyCosts)
promiseValid = false;
}
function returnRemainingMoney() onlyClient {
if (client.receiveEther.value(this.balance)())
promiseValid = false;
}
function getDailyPayment() {
if (msg.sender != contractor)
throw;
uint amount = (now - dateOfSignature) / (1 days) * dailyCosts - paidOut;
if (contractor.send(amount))
paidOut += amount;
}
function setRewardDivisor(uint _rewardDivisor) callingRestriction {
29
30
31