Direct December 2015 ancestor of The DAO, deployed by slock.it five months before the mainnet launch that triggered the Ethereum hard fork.
Historical Significance
The single most historically loaded pre-DAO contract on Ethereum. Five months before The DAO went live (and seven months before it was hacked and triggered the hard fork), the slock.it team was already running the exact same Token + Crowdfunding + DAO governance pattern on mainnet as a live test. The selector surface, contract inheritance structure, and security model that produced the 3.6 million ETH crisis all originate here. Understanding what changed between this December 2015 prototype and the April 2016 production deployment is one of the most important open questions for any researcher studying the DAO incident.
Context
Deployed 29 December 2015, three months before The DAO went live (30 April 2016) and seven months before the DAO hack (17 June 2016) that triggered the Ethereum hard fork (20 July 2016, block 1,920,000). Solidity was at v0.1.7 (latest stable in December 2015); the modern modifier _; syntax did not yet exist and throw was the sole error mechanism. The contract uses the pre-EIP-20 ERC-20 argument order with the sender as the LAST argument of transferFrom, a layout still common in early 2016 token deployments before Fabian Vogelsteller's standard solidified.
Key Facts
Description
This is the December 2015 prototype DAO/Crowdfunding deployment by Christoph Jentzsch's slock.it team, the same architecture that became The DAO on 30 April 2016. The contract is contract DAO is DAOInterface, Token, Crowdfunding(...) from the blockchainsllc/DAO repository, combining a custom ERC-20-precursor Token (with the OLD transferFrom(address from, uint value, address to) argument order, sender last, predating EIP-20), a Crowdfunding base contract, and DAO governance into a single deployment.
The public surface exposes the full pre-DAO design: buyToken() / buyTokenProxy(address) to participate in the crowdsale at 1 ether per token, Crowdfunding(uint, uint) returning the constructor parameters as runtime selectors (a v0.1.x compiler quirk), closingTime() and minValue() for the crowdsale terms, totalAmountReceived() for the running tally, refund() for after-failure refunds, funded() for the success flag, and addAllowedAddress(address) for whitelist control. The DAO layer adds proposals(uint), numProposals(), vote(uint, bool), executeProposal(uint, bytes), checkProposalCode(uint, address, uint, bytes), and changeProposalDeposit(uint). The token layer adds standard balanceOf, transfer, transferFrom, approve, unapprove, plus the now-forgotten approveOnce(address, uint256) and isApprovedOnceFor(address, address) for single-shot delegated transfers.
The deployer 0xb9f40f5b61b5eb9135d268ee0964532f191edab8 was a slock.it test wallet. The 0.88 ETH balance has been stuck since the contract's 42-day closing window expired in early February 2016: minValue was 500,000 ETH and only fractional ETH was contributed, so the crowdsale failed and the contributions could have been pulled back via refund(), but the residual was never claimed.
This is the direct technical ancestor of the contract that triggered the Ethereum hard fork. The same Token + Crowdfunding + DAO inheritance pattern was reused for The DAO on 30 April 2016 at 0xbb9bc244d798123fde783fcc1c72d3bb8c189413.
Source Verified
Byte-for-byte exact bytecode match. Runtime SHA-256 b3f15f4fd3ab702172c3d8cfd4a201b7cca642e75d7762d51278099ce5ac33b1 reproduces with soljson-v0.1.7-nightly.2015.11.19+commit.58110b27 optimizer ON. Required two non-obvious adjustments: (1) deployed source had transfer/transferFrom moved into the DAO contract tail rather than the Token contract; (2) the v0.1.6 RELEASE emits 3*words+15 for the identity-precompile gas formula, while v0.1.7-nightly emits 15*words+3 (libethereum bumped to 1.1.0 between releases and the eth::c_identityGas/eth::c_identityWordGas constants effectively swapped). See proofs/dao-crowdfunding-0x54715db7/README.md and verify.js for full reproducer.
Heuristic Analysis
The following characteristics were detected through bytecode analysis and may not be accurate.
Frontier Era
The initial release of Ethereum. A bare-bones implementation for technical users.
Bytecode Overview
Verified Source Available
Source verified through compiler archaeology and exact bytecode matching.
View Verification ProofShow source code (Solidity)
/*
This creates a Democractic Autonomous Organization. Membership is based
on ownership of custom tokens, which are used to vote on proposals.
This contract is intended for educational purposes, you are fully responsible
for compliance with present or future regulations of finance, communications
and the universal rights of digital beings.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org>
*/
/*
Most, basic default, standardised Token contract. No "pre-mine". Tokens need to be created by a derived contract (e.g. crowdsale)
Original taken from gist.github.com/simondlr/9a9c658d4f5f8c2e88fd
which is based on standardised APIs: https://github.com/ethereum/wiki/wiki/Standardized_Contract_APIs
.*/
/// @title Standard Token Contract.
contract TokenInterface {
/// @notice send `_value` token to `_to` from `msg.sender`
/// @param _value The amount of token to be transfered
/// @param _to The address of the recipient
/// @return Whether the transfer was successful or not
function transfer(uint _value, address _to) returns (bool _success) {}
/// @notice send `_value` token to `_to` from `_from`
/// @param _value The amount of token to be transfered
/// @param _to The address of the recipient
/// @param _from The address of the sender
/// @return Whether the transfer was successful or not
function transferFrom(address _from, uint _value, address _to) returns (bool _success) {}
/// @param _addr The Address from which the balance will be retrieved
/// @return The balance
function balanceOf(address _addr) constant returns (uint _r) {}
/// @notice `msg.sender` approves `_addr` to transfer his tokens
/// @param _addr The address of the account able to transfer the tokens
/// @return Whether the approval was successful or not
function approve(address _addr) returns (bool _success) {}
/// @notice `msg.sender` unapproves `_addr` to tranfers his token
/// @param _addr The address of the account now unable to transfer the tokens
/// @return Whether the unapproval was successful or not
function unapprove(address _addr) returns (bool _success) {}
/// @param _target The address of the account who gave approval
/// @param _proxy The address of the account who has apptoval
/// @return Whether `_proxy` has approval to transfer tokens in behalf of `_target` or not
function isApprovedFor(address _target, address _proxy) constant returns (bool _r) {}
/// @notice `msg.sender` approves once that `_maxValue`tokens can be transfered by `_addr`
/// @param _addr The address of the account able to transfer the tokens
/// @param _maxValue The amount of Token to be approved for transferal
/// @return Whether the approval was successful or not
function approveOnce(address _addr, uint256 _maxValue) returns (bool _success) {}
/// @param _target The address of the account who gave a one time approval
/// @param _proxy The address of the account who has a one time apptoval
/// @return Whether `_proxy` has a one time approval to transfer tokens in behalf of `_target` or not
function isApprovedOnceFor(address _target, address _proxy) constant returns (uint _maxValue) {}
event Transfer(address indexed from, address indexed to, uint256 value);
event AddressApproval(address indexed addr, address indexed proxy, bool result);
event AddressApprovalOnce(address indexed addr, address indexed proxy, uint256 value);
}
contract Token is TokenInterface {
//explicitly not publicly accessible. Should rely on methods for purpose of standardization.
mapping (address => uint) balances;
mapping (address => mapping (address => bool)) approved;
mapping (address => mapping (address => uint256)) approved_once;
function balanceOf(address _addr) constant returns (uint _r) {
return balances[_addr];
}
function approve(address _addr) returns (bool _success) {
approved[msg.sender][_addr] = true;
AddressApproval(msg.sender, _addr, true);
return true;
}
function unapprove(address _addr) returns (bool _success) {
approved[msg.sender][_addr] = false;
approved_once[msg.sender][_addr] = 0;
//debatable whether to include...
AddressApproval(msg.sender, _addr, false);
AddressApprovalOnce(msg.sender, _addr, 0);
}
function isApprovedFor(address _target, address _proxy) constant returns (bool _r) {
return approved[_target][_proxy];
}
function approveOnce(address _addr, uint256 _maxValue) returns (bool _success) {
approved_once[msg.sender][_addr] = _maxValue;
AddressApprovalOnce(msg.sender, _addr, _maxValue);
return true;
}
function isApprovedOnceFor(address _target, address _proxy) constant returns (uint _maxValue) {
return approved_once[_target][_proxy];
}
}
/*
This creates a Democractic Autonomous Organization. Membership is based
on ownership of custom tokens, which are used to vote on proposals.
This contract is intended for educational purposes, you are fully responsible
for compliance with present or future regulations of finance, communications
and the universal rights of digital beings.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org>
*/
/*Basic crowdsale contract. Allows to sale Tokens for the price of one Ether */
contract CrowdfundingInterface {
/// @dev Constructor setting the minimal target and the end of the crowdsale
/// @param _minValue Minimal value for a successful crowdfunding
/// @param _closingTime Date (in unix time) of the end of the crowdsale
function Crowdfunding(uint _minValue, uint _closingTime) {}
/// @notice Buy token with `msg.sender` as the beneficiary. One ether creates one token (same base units)
function buyToken() {}
/// @notice Buy token with `_beneficiary` as the beneficiary. One ether creates one token (same base units)
/// @param _beneficiary The beneficary of for the token bought with ether
function buyTokenProxy(address _beneficiary) {}
/// @notice Refund `msg.sender` in the case of a not successful crowdfunding
function refund() {}
event Funded(uint value);
event SoldToken(address to, uint value);
event Refund(address to, uint value);
}
contract Crowdfunding is CrowdfundingInterface, Token {
uint public closingTime; // end of crowdfunding
uint public minValue; // minimal goal of crowdfunding
uint public totalAmountReceived; // total amount of received wei in the crowdfunding
bool public funded;
function Crowdfunding(uint _minValue, uint _closingTime) {
closingTime = _closingTime;
minValue = _minValue;
}
function buyToken() {
buyTokenProxy(msg.sender);
}
function buyTokenProxy(address _beneficiary) {
if (now < closingTime) {
balances[_beneficiary] += msg.value;
totalAmountReceived += msg.value;
SoldToken(_beneficiary, msg.value);
if (totalAmountReceived >= minValue && !funded) {
funded = true;
Funded(totalAmountReceived);
}
}
}
function refund() {
if (now > closingTime
&& !funded
&& msg.sender.send(balances[msg.sender])) // execute refund
{
balances[msg.sender] = 0;
Refund(msg.sender, msg.value);
}
}
}
/*
This creates a Democractic Autonomous Organization. Membership is based
on ownership of custom tokens, which are used to vote on proposals.
This contract is intended for educational purposes, you are fully responsible
for compliance with present or future regulations of finance, communications
and the universal rights of digital beings.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org>
*/
/*
TODO:
better debatingPeriod mechanism (based on amount, min, max, ...)
include tx.data in proposal?
min quorum / deposit
*/
contract DAOInterface {
modifier onlyShareholders {}
/// @dev Constructor setting the default service provider and the address for the contract able to create another DAO
/// @param _defaultServiceProvider The default service provider
/// @param _daoCreator The contract able to (re)create this DAO
function _DAO(address _defaultServiceProvider, DAO_Creator _daoCreator) {} // the underscore is only because the constructor can not be overloaded
/// @notice `msg.sender` creates a proposal to send `_etherAmount` ether to `_recipient` with the transaction data `_transactionBytecode`. (If this is true: `_newServiceProvider` , then this is a proposal the set `_recipient` as the new service provider)
/// @param _recipient The address of the recipient of the proposed transaction
/// @param _etherAmount The amount of ether to be sent with the proposed transaction
/// @param _description A string descibing the proposal
/// @param _transactionBytecode The data of the proposed transaction
/// @param _newServiceProvider A bool defining whether this proposal is about a new service provider or not
/// @return The proposal ID. Needed for voting on the proposal
function newProposal(address _recipient, uint _etherAmount, string _description, bytes _transactionBytecode, bool _newServiceProvider) onlyShareholders returns (uint _proposalID) {}
/// @notice Check that the proposal with the ID `_proposalID` matches a transaction which sends `_etherAmount` with this data: `_transactionBytecode` to `_recipient`
/// @param _proposalID The proposal ID
/// @param _recipient The recipient of the proposed transaction
/// @param _etherAmount The amount of ether to be sent with the proposed transaction
/// @param _transactionBytecode The data of the proposed transaction
/// @return Whether the proposal ID matches the transaction data or not
function checkProposalCode(uint _proposalID, address _recipient, uint _etherAmount, bytes _transactionBytecode) constant returns (bool _codeChecksOut) {}
/// @notice Vote on proposal `_proposalID` with `_supportsProposal`
/// @param _proposalID The proposal ID
/// @param _supportsProposal Yes/No - support of the proposal
/// @return The proposal ID.
function vote(uint _proposalID, bool _supportsProposal) onlyShareholders returns (uint _voteID){}
/// @notice Checks whether proposal `_proposalID` with transaction data `_transactionBytecode` has been voted for or against it, and executes the transaction in the case it has been voted for.
/// @param _proposalID The proposal ID
/// @param _transactionBytecode The data of the proposed transaction
/// @return Whether the proposed transaction has been executed or not
function executeProposal(uint _proposalID, bytes _transactionBytecode) returns (bool _success) {}
/// @notice ATTENTION! I confirm to move my remaining funds to a new DAO with `_newServiceProvider` as the new service provider, as has been proposed in proposal `_proposalID`. This will burn the portion of my tokens according to the funds the DAO has already spent. This can not be undone and will split the DAO into two DAO's, with two underlying tokens.
/// @param _proposalID The proposal ID
/// @param _newServiceProvider The new service provider of the new DAO
/// @dev This function, when called for the first time for this proposal, will create a new DAO and send the portion of the remaining funds which can be attributed to the sender to the new DAO. It will also burn the tokens of the sender according the unspent funds of the DAO.
function confirmNewServiceProvider(uint _proposalID, address _newServiceProvider) {}
/// @notice add new possible recipient `_recipient` for transactions from the DAO (through proposals)
/// @param _recipient New recipient address
/// @dev Can only be called by the current service provider
function addAllowedAddress(address _recipient) external {}
/// @notice change the depsoit needed to make a proposal to `_proposalDeposit`
/// @param _proposalDeposit New proposal deposit
/// @dev Can only be called by the service provider
function changeProposalDeposit(uint _proposalDeposit) external {}
event ProposalAdded(uint proposalID, address recipient, uint amount, string description);
event Voted(uint proposalID, bool position, address voter);
event ProposalTallied(uint proposalID, bool result, uint quorum, bool active);
event NewServiceProvider(address _newServiceProvider);
event AllowedRecipientAdded(address _recipient);
}
/* The democracy contract itself */
contract DAO is DAOInterface, Token, Crowdfunding(500000 ether, now + 42 days) { // I would rather use the dynamic initialization instead of the static one (see construcitor), but doesn't work yet, due to a bug in Solidity
/* Contract Variables and events */
Proposal[] public proposals;
uint public numProposals;
uint dividends;
address serviceProvider;
address[] allowedRecipients;
// deposit in Ether to be paid for each proposal
uint proposalDeposit;
DAO_Creator daoCreator;
struct Proposal {
address recipient;
uint amount;
string description;
uint votingDeadline;
bool openToVote;
bool proposalPassed;
uint numberOfVotes;
bytes32 proposalHash;
bool newServiceProvider;
DAO newDAO;
Vote[] votes;
mapping (address => bool) voted;
address creator;
}
struct Vote {
bool inSupport;
address voter;
}
// modifier that allows only shareholders to vote and create new proposals
modifier onlyShareholders {
if (balanceOf(msg.sender) == 0) throw;
_
}
// modifier that allows only the servie provider to add new allowed addresses and change the proposal deposit
modifier onlyServiceProvider {
if (msg.sender != serviceProvider) throw;
_
}
function() {
dividends += msg.value;
}
// I would rather use the dynamic initialization instead of the static one (see declaration above), but doesn't work yet, due to a bug in Solidity
//function DAO(address defaultServiceProvider, DAO_Creator _daoCreator, uint _minValue, uint _closingTime) Crowdfunding(_minValue, _closingTime) {
function DAO(address _defaultServiceProvider, DAO_Creator _daoCreator) {
serviceProvider = _defaultServiceProvider;
daoCreator = _daoCreator;
proposalDeposit = 100 ether;
}
function newProposal(address _recipient, uint _etherAmount, string _description, bytes _transactionBytecode, bool _newServiceProvider) onlyShareholders returns (uint _proposalID) {
// check sanity
if (_newServiceProvider && (_etherAmount != 0 || _transactionBytecode.length != 0 || _recipient == serviceProvider)) {
throw;
}
else if (!isRecipientAllowed(_recipient)) throw;
if (!funded || msg.value < proposalDeposit) throw;
_proposalID = proposals.length++;
Proposal p = proposals[_proposalID];
p.recipient = _recipient;
p.amount = _etherAmount;
p.description = _description;
p.proposalHash = sha3(_recipient, _etherAmount, _transactionBytecode);
p.votingDeadline = now + debatingPeriod(_newServiceProvider, _etherAmount * 1 ether);
p.openToVote = true;
p.proposalPassed = false;
p.numberOfVotes = 0;
p.newServiceProvider = _newServiceProvider;
p.creator = msg.sender;
ProposalAdded(_proposalID, _recipient, _etherAmount, _description);
numProposals = _proposalID + 1;
}
function checkProposalCode(uint _proposalNumber, address _recipient, uint _etherAmount, bytes _transactionBytecode) constant returns (bool _codeChecksOut) {
Proposal p = proposals[_proposalNumber];
return p.proposalHash == sha3(_recipient, _etherAmount, _transactionBytecode);
}
function vote(uint _proposalNumber, bool _supportsProposal) onlyShareholders returns (uint _voteID){
Proposal p = proposals[_proposalNumber];
if (p.voted[msg.sender] == true) throw;
_voteID = p.votes.length++;
p.votes[_voteID] = Vote({inSupport: _supportsProposal, voter: msg.sender});
p.voted[msg.sender] = true;
p.numberOfVotes = _voteID + 1;
Voted(_proposalNumber, _supportsProposal, msg.sender);
}
function executeProposal(uint _proposalNumber, bytes _transactionBytecode) returns (bool _success) {
Proposal p = proposals[_proposalNumber];
// Check if the proposal can be executed
if (now < p.votingDeadline // has the voting deadline arrived?
|| !p.openToVote // has it been already executed?
|| p.proposalHash != sha3(p.recipient, p.amount, _transactionBytecode) // Does the transaction code match the proposal?
|| p.newServiceProvider) // is it a new service provider proposal
throw;
// tally the votes
uint quorum = 0;
uint yea = 0;
uint nay = 0;
for (uint i = 0; i < p.votes.length; ++i) {
Vote v = p.votes[i];
uint voteWeight = balanceOf(v.voter);
quorum += voteWeight;
if (v.inSupport)
yea += voteWeight;
else
nay += voteWeight;
}
// execute result
if (quorum >= minQuorum(p.newServiceProvider, p.amount) && yea > nay ) {
// has quorum and was approved
if (p.recipient.call.value(p.amount * 1 ether)(_transactionBytecode)) {
p.openToVote = false;
p.proposalPassed = true;
_success = true;
p.creator.send(proposalDeposit);
}
} else if (quorum >= minQuorum(p.newServiceProvider, p.amount) && nay > yea) {
p.openToVote = false;
p.proposalPassed = false;
p.creator.send(proposalDeposit);
}
// fire event
ProposalTallied(_proposalNumber, _success, quorum, p.openToVote);
}
function confirmNewServiceProvider(uint _proposalNumber, address _newServiceProvider) onlyShareholders {
Proposal p = proposals[_proposalNumber];
// sanity check
if (now < p.votingDeadline /* has the voting deadline arrived? */
|| p.proposalHash != sha3(p.recipient, 0, 0) /* Does the transaction code match the proposal? */
|| !p.newServiceProvider // is it a new service provider proposale
|| p.recipient != _newServiceProvider)
throw;
// if not already happend, create new DAO
if (address(p.newDAO) == 0)
p.newDAO = createNewDAO(_newServiceProvider);
// move funds and assign new Tokens
p.newDAO.buyTokenProxy.value(balanceOf(msg.sender) * this.balance / (totalAmountReceived + dividends))(msg.sender);
// burn Slock tokens
balances[msg.sender] *= (1 - this.balance / (totalAmountReceived + dividends));
}
function addAllowedAddress(address _recipient) onlyServiceProvider external {
allowedRecipients.push(_recipient);
}
function changeProposalDeposit(uint _proposalDeposit) onlyServiceProvider external {
proposalDeposit = _proposalDeposit;
}
function isRecipientAllowed(address recipient) internal returns (bool _isAllowed) {
if (recipient == serviceProvider)
return true;
for (uint i = 0; i < allowedRecipients.length; ++i) {
if (recipient == allowedRecipients[i])
return true;
}
return false;
}
function debatingPeriod(bool _newServiceProvider, uint _value) internal returns (uint _debatingPeriod) {
if (_newServiceProvider)
return 61 days;
else
return 1 weeks + (_value * 31 days) / totalAmountReceived;
}
function minQuorum(bool _newServiceProvider, uint _value) internal returns (uint _minQuorum) {
if (_newServiceProvider)
return totalAmountReceived / 2;
else
return totalAmountReceived / 5 + _value / 3;
}
function createNewDAO(address _newServiceProvider) internal returns (DAO _newDAO) {
NewServiceProvider(_newServiceProvider);
return daoCreator.createDAO(_newServiceProvider, daoCreator);
}
function transfer(uint _value, address _to) returns (bool _success) {
if (balances[msg.sender] >= _value) {
balances[msg.sender] -= _value;
balances[_to] += _value;
Transfer(msg.sender, _to, _value);
return true;
}
else
return false;
}
function transferFrom(address _from, uint _value, address _to) returns (bool _success) {
if (balances[_from] >= _value) {
bool transfer = false;
if (approved[_from][msg.sender]) {
transfer = true;
}
else if (_value <= approved_once[_from][msg.sender]) {
transfer = true;
approved_once[_from][msg.sender] = 0; //reset
}
if (transfer == true) {
balances[_from] -= _value;
balances[_to] += _value;
Transfer(_from, _to, _value);
return true;
}
else
return false;
}
else
return false;
}
}
contract DAO_Creator {
function createDAO(address _defaultServiceProvider, DAO_Creator _daoCreator) returns (DAO _newDAO) {
return new DAO(_defaultServiceProvider, _daoCreator);
}
}