solidity/test/externalTests/solc-js/DAO/DAO.sol

947 lines
36 KiB
Solidity
Raw Normal View History

/*
This file is part of the DAO.
The DAO is free software: you can redistribute it and/or modify
it under the terms of the GNU lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
The DAO is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU lesser General Public License for more details.
You should have received a copy of the GNU lesser General Public License
along with the DAO. If not, see <http://www.gnu.org/licenses/>.
*/
/*
Standard smart contract for a Decentralized Autonomous Organization (DAO)
to automate organizational governance and decision-making.
*/
import "./TokenCreation.sol";
import "./ManagedAccount.sol";
2019-10-23 20:10:12 +00:00
abstract contract DAOInterface {
// The amount of days for which people who try to participate in the
// creation by calling the fallback function will still get their ether back
uint constant creationGracePeriod = 40 days;
// The minimum debate period that a generic proposal can have
uint constant minProposalDebatePeriod = 2 weeks;
// The minimum debate period that a split proposal can have
uint constant minSplitDebatePeriod = 1 weeks;
// Period of days inside which it's possible to execute a DAO split
uint constant splitExecutionPeriod = 27 days;
// Period of time after which the minimum Quorum is halved
uint constant quorumHalvingPeriod = 25 weeks;
// Period after which a proposal is closed
// (used in the case `executeProposal` fails because it throws)
uint constant executeProposalPeriod = 10 days;
// Denotes the maximum proposal deposit that can be given. It is given as
// a fraction of total Ether spent plus balance of the DAO
uint constant maxDepositDivisor = 100;
// Proposals to spend the DAO's ether or to choose a new Curator
Proposal[] public proposals;
// The quorum needed for each proposal is partially calculated by
// totalSupply / minQuorumDivisor
uint public minQuorumDivisor;
// The unix time of the last time quorum was reached on a proposal
uint public lastTimeMinQuorumMet;
// Address of the curator
address payable public curator;
// The whitelist: List of addresses the DAO is allowed to send ether to
mapping (address => bool) public allowedRecipients;
// Tracks the addresses that own Reward Tokens. Those addresses can only be
// DAOs that have split from the original DAO. Conceptually, Reward Tokens
// represent the proportion of the rewards that the DAO has the right to
// receive. These Reward Tokens are generated when the DAO spends ether.
mapping (address => uint) public rewardToken;
// Total supply of rewardToken
uint public totalRewardToken;
// The account used to manage the rewards which are to be distributed to the
// DAO Token Holders of this DAO
ManagedAccount public rewardAccount;
// The account used to manage the rewards which are to be distributed to
// any DAO that holds Reward Tokens
ManagedAccount public DAOrewardAccount;
// Amount of rewards (in wei) already paid out to a certain DAO
mapping (address => uint) public DAOpaidOut;
// Amount of rewards (in wei) already paid out to a certain address
mapping (address => uint) public paidOut;
// Map of addresses blocked during a vote (not allowed to transfer DAO
// tokens). The address points to the proposal ID.
mapping (address => uint) public blocked;
// The minimum deposit (in wei) required to submit any proposal that is not
// requesting a new Curator (no deposit is required for splits)
uint public proposalDeposit;
// the accumulated sum of all current proposal deposits
uint sumOfProposalDeposits;
// Contract that is able to create a new DAO (with the same code as
// this one), used for splits
DAO_Creator public daoCreator;
// A proposal with `newCurator == false` represents a transaction
// to be issued by this DAO
// A proposal with `newCurator == true` represents a DAO split
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 payable 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 proposal's 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 payable 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;
}
/// @dev Constructor setting the Curator and the address
/// for the contract able to create another DAO as well as the parameters
/// for the DAO Token Creation
/// @param _curator The Curator
/// @param _daoCreator The contract able to (re)create this DAO
/// @param _proposalDeposit The deposit to be paid for a regular proposal
/// @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 DAO Token Creation
/// @param _privateCreation If zero the DAO Token Creation is open to public, a
/// non-zero address means that the DAO Token Creation is only for the address
/// @param _tokenName The name that the DAO's token will have
/// @param _tokenSymbol The ticker symbol that this DAO token should have
/// @param _decimalPlaces The number of decimal places that the token is
/// counted from.
// This is the constructor: it can not be overloaded so it is commented out
// function DAO(
// address payable _curator,
// DAO_Creator _daoCreator,
// uint _proposalDeposit,
// uint _minTokensToCreate,
// uint _closingTime,
// address _privateCreation
// string _tokenName,
// string _tokenSymbol,
// uint8 _decimalPlaces
// );
/// @notice Create Token with `msg.sender` as the beneficiary
2019-11-05 17:25:34 +00:00
receive() virtual external payable;
/// @dev This function is used to send ether back
/// to the DAO, it can also be used to receive payments that should not be
/// counted as rewards (donations, grants, etc.)
/// @return Whether the DAO received the ether successfully
2019-11-05 17:25:34 +00:00
function receiveEther() public virtual returns (bool);
/// @notice `msg.sender` creates a proposal to send `_amount` Wei to
/// `_recipient` with the transaction data `_transactionData`. If
/// `_newCurator` is true, then this is a proposal that splits the
/// DAO and sets `_recipient` as the new DAO's Curator.
/// @param _recipient Address of the recipient of the proposed transaction
/// @param _amount Amount of wei to be sent with the proposed transaction
/// @param _description String describing the proposal
/// @param _transactionData Data of the proposed transaction
/// @param _debatingPeriod Time used for debating a proposal, at least 2
/// weeks for a regular proposal, 10 days for new Curator proposal
/// @param _newCurator Bool defining whether this proposal is about
/// a new Curator or not
/// @return _proposalID The proposal ID. Needed for voting on the proposal
function newProposal(
address payable _recipient,
uint _amount,
string memory _description,
bytes memory _transactionData,
uint _debatingPeriod,
bool _newCurator
2019-11-05 17:25:34 +00:00
) public virtual payable returns (uint _proposalID);
/// @notice Check that the proposal with the ID `_proposalID` matches the
/// transaction which sends `_amount` with data `_transactionData`
/// to `_recipient`
/// @param _proposalID The proposal ID
/// @param _recipient The recipient of the proposed transaction
/// @param _amount The amount of wei to be sent in the proposed transaction
/// @param _transactionData The data of the proposed transaction
/// @return _codeChecksOut Whether the proposal ID matches the transaction data or not
function checkProposalCode(
uint _proposalID,
address payable _recipient,
uint _amount,
bytes memory _transactionData
2019-11-05 17:25:34 +00:00
) public virtual view returns (bool _codeChecksOut);
/// @notice Vote on proposal `_proposalID` with `_supportsProposal`
/// @param _proposalID The proposal ID
/// @param _supportsProposal Yes/No - support of the proposal
/// @return _voteID The vote ID.
function vote(
uint _proposalID,
bool _supportsProposal
2019-11-05 17:25:34 +00:00
) public virtual 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 _success Whether the proposed transaction has been executed or not
function executeProposal(
uint _proposalID,
bytes memory _transactionData
2019-11-05 17:25:34 +00:00
) public virtual 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 DAO's, 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 sender's 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 payable _newCurator
2019-11-05 17:25:34 +00:00
) public virtual 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
2019-11-05 17:25:34 +00:00
function newContract(address payable _newContract) virtual public;
/// @notice Add a new possible recipient `_recipient` to the whitelist so
/// that the DAO can send transactions to them (using proposals)
/// @param _recipient New recipient address
/// @dev Can only be called by the current Curator
/// @return _success Whether successful or not
2019-11-05 17:25:34 +00:00
function changeAllowedRecipients(address _recipient, bool _allowed) virtual external returns (bool _success);
/// @notice Change the minimum deposit required to submit a proposal
/// @param _proposalDeposit The new proposal deposit
/// @dev Can only be called by this DAO (through proposals with the
/// recipient being this DAO itself)
2019-11-05 17:25:34 +00:00
function changeProposalDeposit(uint _proposalDeposit) virtual external;
/// @notice Move rewards from the DAORewards managed account
/// @param _toMembers If true rewards are moved to the actual reward account
/// for the DAO. If not then it's moved to the DAO itself
/// @return _success Whether the call was successful
2019-11-05 17:25:34 +00:00
function retrieveDAOReward(bool _toMembers) virtual external returns (bool _success);
/// @notice Get my portion of the reward that was sent to `rewardAccount`
/// @return _success Whether the call was successful
2019-11-05 17:25:34 +00:00
function getMyReward() public virtual returns (bool _success);
/// @notice Withdraw `_account`'s portion of the reward from `rewardAccount`
/// to `_account`'s balance
/// @return _success Whether the call was successful
2019-11-05 17:25:34 +00:00
function withdrawRewardFor(address payable _account) internal virtual returns (bool _success);
/// @notice Send `_amount` tokens to `_to` from `msg.sender`. Prior to this
/// getMyReward() is called.
/// @param _to The address of the recipient
2019-09-25 14:00:26 +00:00
/// @param _amount The amount of tokens to be transferred
/// @return success Whether the transfer was successful or not
2019-11-05 17:25:34 +00:00
function transferWithoutReward(address _to, uint256 _amount) public virtual returns (bool success);
/// @notice Send `_amount` tokens to `_to` from `_from` on the condition it
/// is approved by `_from`. Prior to this getMyReward() is called.
/// @param _from The address of the sender
/// @param _to The address of the recipient
2019-09-25 14:00:26 +00:00
/// @param _amount The amount of tokens to be transferred
/// @return success Whether the transfer was successful or not
function transferFromWithoutReward(
address payable _from,
address _to,
uint256 _amount
2019-11-05 17:25:34 +00:00
) public virtual returns (bool success);
/// @notice Doubles the 'minQuorumDivisor' in the case quorum has not been
/// achieved in 52 weeks
/// @return _success Whether the change was successful or not
2019-11-05 17:25:34 +00:00
function halveMinQuorum() public virtual returns (bool _success);
/// @return _numberOfProposals total number of proposals ever created
2019-11-05 17:25:34 +00:00
function numberOfProposals() public virtual view returns (uint _numberOfProposals);
/// @param _proposalID Id of the new curator proposal
/// @return _newDAO Address of the new DAO
2019-11-05 17:25:34 +00:00
function getNewDAOAddress(uint _proposalID) public virtual view returns (address _newDAO);
/// @param _account The address of the account which is checked.
/// @return Whether the account is blocked (not allowed to transfer tokens) or not.
2019-11-05 17:25:34 +00:00
function isBlocked(address _account) internal virtual returns (bool);
/// @notice If the caller is blocked by a proposal whose voting deadline
/// has exprired then unblock him.
/// @return Whether the account is blocked (not allowed to transfer tokens) or not.
2019-11-05 17:25:34 +00:00
function unblockMe() public virtual 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 AllowedRecipientChanged(address indexed _recipient, bool _allowed);
}
// The DAO contract itself
contract DAO is DAOInterface, Token, TokenCreation {
// Modifier that allows only shareholders to vote and create new proposals
modifier onlyTokenholders {
if (balanceOf(msg.sender) == 0) revert();
_;
}
constructor(
address payable _curator,
DAO_Creator _daoCreator,
uint _proposalDeposit,
uint _minTokensToCreate,
uint _closingTime,
address _privateCreation,
string memory _tokenName,
string memory _tokenSymbol,
uint8 _decimalPlaces
) TokenCreation(
_minTokensToCreate,
_closingTime,
_privateCreation,
_tokenName,
_tokenSymbol,
_decimalPlaces) public {
curator = _curator;
daoCreator = _daoCreator;
proposalDeposit = _proposalDeposit;
rewardAccount = new ManagedAccount(address(this), false);
DAOrewardAccount = new ManagedAccount(address(this), false);
if (address(rewardAccount) == 0x0000000000000000000000000000000000000000)
revert();
if (address(DAOrewardAccount) == 0x0000000000000000000000000000000000000000)
revert();
lastTimeMinQuorumMet = block.timestamp;
minQuorumDivisor = 5; // sets the minimal quorum to 20%
2019-09-25 14:00:26 +00:00
proposals.push(); // avoids a proposal with ID 0 because it is used
allowedRecipients[address(this)] = true;
allowedRecipients[curator] = true;
}
receive() external payable override(DAOInterface, TokenCreation) {
if (block.timestamp < closingTime + creationGracePeriod && msg.sender != address(extraBalance))
createTokenProxy(payable(msg.sender));
else
receiveEther();
}
2019-09-16 12:33:43 +00:00
function receiveEther() public override returns (bool) {
return true;
}
function newProposal(
address payable _recipient,
uint _amount,
string memory _description,
bytes memory _transactionData,
uint _debatingPeriod,
bool _newCurator
2019-09-16 12:33:43 +00:00
) onlyTokenholders public override payable returns (uint _proposalID) {
// Sanity check
if (_newCurator && (
_amount != 0
|| _transactionData.length != 0
|| _recipient == curator
|| msg.value > 0
|| _debatingPeriod < minSplitDebatePeriod)) {
revert();
} else if (
!_newCurator
&& (!isRecipientAllowed(_recipient) || (_debatingPeriod < minProposalDebatePeriod))
) {
revert();
}
if (_debatingPeriod > 8 weeks)
revert();
if (!isFueled
|| block.timestamp < closingTime
|| (msg.value < proposalDeposit && !_newCurator)) {
revert();
}
if (block.timestamp + _debatingPeriod < block.timestamp) // prevents overflow
revert();
// to prevent a 51% attacker to convert the ether into deposit
if (msg.sender == address(this))
revert();
// to prevent curator from halving quorum before first proposal
if (proposals.length == 1) // initial length is 1 (see constructor)
lastTimeMinQuorumMet = block.timestamp;
2019-09-25 14:00:26 +00:00
Proposal storage p = proposals.push();
_proposalID = proposals.length - 1;
p.recipient = _recipient;
p.amount = _amount;
p.description = _description;
p.proposalHash = keccak256(abi.encodePacked(_recipient, _amount, _transactionData));
p.votingDeadline = block.timestamp + _debatingPeriod;
p.open = true;
//p.proposalPassed = False; // that's default
p.newCurator = _newCurator;
if (_newCurator)
2019-09-25 14:00:26 +00:00
p.splitData.push();
p.creator = payable(msg.sender);
p.proposalDeposit = msg.value;
sumOfProposalDeposits += msg.value;
emit ProposalAdded(
_proposalID,
_recipient,
_amount,
_newCurator,
_description
);
}
function checkProposalCode(
uint _proposalID,
address payable _recipient,
uint _amount,
bytes memory _transactionData
2019-09-16 12:33:43 +00:00
) public override view returns (bool _codeChecksOut) {
Proposal storage p = proposals[_proposalID];
return p.proposalHash == keccak256(abi.encodePacked(_recipient, _amount, _transactionData));
}
function vote(
uint _proposalID,
bool _supportsProposal
2019-09-16 12:33:43 +00:00
) onlyTokenholders public override returns (uint _voteID) {
Proposal storage p = proposals[_proposalID];
if (p.votedYes[msg.sender]
|| p.votedNo[msg.sender]
|| block.timestamp >= p.votingDeadline) {
revert();
}
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 proposal's voting deadline is further into the future than
// the proposal that blocks the sender so make it the blocker
blocked[msg.sender] = _proposalID;
}
emit Voted(_proposalID, _supportsProposal, msg.sender);
}
function executeProposal(
uint _proposalID,
bytes memory _transactionData
2019-09-16 12:33:43 +00:00
) public override returns (bool _success) {
Proposal storage p = proposals[_proposalID];
uint waitPeriod = p.newCurator
? splitExecutionPeriod
: executeProposalPeriod;
// If we are over deadline and waiting period, assert proposal is closed
if (p.open && block.timestamp > p.votingDeadline + waitPeriod) {
closeProposal(_proposalID);
return false;
}
// Check if the proposal can be executed
if (block.timestamp < p.votingDeadline // has the voting deadline arrived?
// Have the votes been counted?
|| !p.open
|| p.proposalPassed // anyone trying to call us recursively?
// Does the transaction code match the proposal?
|| p.proposalHash != keccak256(abi.encodePacked(p.recipient, p.amount, _transactionData))) {
revert();
}
// 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 false;
}
bool proposalCheck = true;
if (p.amount > actualBalance())
proposalCheck = false;
uint quorum = p.yea + p.nay;
// require 53% for calling newContract()
if (_transactionData.length >= 4 && _transactionData[0] == 0x68
&& _transactionData[1] == 0x37 && _transactionData[2] == 0xff
&& _transactionData[3] == 0x1e
&& quorum < minQuorum(actualBalance() + rewardToken[address(this)])) {
proposalCheck = false;
}
if (quorum >= minQuorum(p.amount)) {
if (!p.creator.send(p.proposalDeposit))
revert();
lastTimeMinQuorumMet = block.timestamp;
// set the minQuorum to 20% again, in the case it has been reached
if (quorum > totalSupply / 5)
minQuorumDivisor = 5;
}
// Execute result
if (quorum >= minQuorum(p.amount) && p.yea > p.nay && proposalCheck) {
// we are setting this here before the CALL() value transfer to
// assure that in the case of a malicious recipient contract trying
// to call executeProposal() recursively money can't be transferred
// multiple times out of the DAO
p.proposalPassed = true;
2020-04-03 14:29:17 +00:00
(bool success,) = p.recipient.call{value: p.amount}(_transactionData);
if (!success)
revert();
_success = true;
// only create reward tokens when ether is not sent to the DAO itself and
// related addresses. Proxy addresses should be forbidden by the curator.
if (p.recipient != address(this) && p.recipient != address(rewardAccount)
&& p.recipient != address(DAOrewardAccount)
&& p.recipient != address(extraBalance)
&& p.recipient != address(curator)) {
rewardToken[address(this)] += p.amount;
totalRewardToken += p.amount;
}
}
closeProposal(_proposalID);
// Initiate event
emit ProposalTallied(_proposalID, _success, quorum);
}
function closeProposal(uint _proposalID) internal {
Proposal storage p = proposals[_proposalID];
if (p.open)
sumOfProposalDeposits -= p.proposalDeposit;
p.open = false;
}
function splitDAO(
uint _proposalID,
address payable _newCurator
2019-09-16 12:33:43 +00:00
) onlyTokenholders public override returns (bool _success) {
Proposal storage p = proposals[_proposalID];
// Sanity check
if (block.timestamp < p.votingDeadline // has the voting deadline arrived?
//The request for a split expires XX days after the voting deadline
|| block.timestamp > 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) ) {
revert();
}
// If the new DAO doesn't exist yet, create the new DAO and store the
// current split data
if (address(p.splitData[0].newDAO) == 0x0000000000000000000000000000000000000000) {
p.splitData[0].newDAO = createNewDAO(_newCurator);
// Call depth limit reached, etc.
if (address(p.splitData[0].newDAO) == 0x0000000000000000000000000000000000000000)
revert();
// should never happen
if (address(this).balance < sumOfProposalDeposits)
revert();
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}(payable(msg.sender)) == false)
revert();
// Assign reward rights to new DAO
uint rewardTokenToBeMoved =
(balances[msg.sender] * p.splitData[0].rewardToken) /
p.splitData[0].totalSupply;
uint paidOutToBeMoved = DAOpaidOut[address(this)] * rewardTokenToBeMoved /
rewardToken[address(this)];
rewardToken[address(p.splitData[0].newDAO)] += rewardTokenToBeMoved;
if (rewardToken[address(this)] < rewardTokenToBeMoved)
revert();
rewardToken[address(this)] -= rewardTokenToBeMoved;
DAOpaidOut[address(p.splitData[0].newDAO)] += paidOutToBeMoved;
if (DAOpaidOut[address(this)] < paidOutToBeMoved)
revert();
DAOpaidOut[address(this)] -= paidOutToBeMoved;
// Burn DAO Tokens
emit Transfer(msg.sender, 0x0000000000000000000000000000000000000000, balances[msg.sender]);
withdrawRewardFor(payable(msg.sender)); // be nice, and get his rewards
totalSupply -= balances[msg.sender];
balances[msg.sender] = 0;
paidOut[msg.sender] = 0;
return true;
}
2019-09-16 12:33:43 +00:00
function newContract(address payable _newContract) public override {
if (msg.sender != address(this) || !allowedRecipients[_newContract]) return;
// move all ether
2020-04-03 14:29:17 +00:00
(bool success,) = _newContract.call{value: address(this).balance}("");
if (!success) {
revert();
}
//move all reward tokens
rewardToken[_newContract] += rewardToken[address(this)];
rewardToken[address(this)] = 0;
DAOpaidOut[_newContract] += DAOpaidOut[address(this)];
DAOpaidOut[address(this)] = 0;
}
2019-09-16 12:33:43 +00:00
function retrieveDAOReward(bool _toMembers) external override returns (bool _success) {
DAO dao = DAO(payable(msg.sender));
if ((rewardToken[msg.sender] * DAOrewardAccount.accumulatedInput()) /
totalRewardToken < DAOpaidOut[msg.sender])
revert();
uint reward =
(rewardToken[msg.sender] * DAOrewardAccount.accumulatedInput()) /
totalRewardToken - DAOpaidOut[msg.sender];
reward = address(DAOrewardAccount).balance < reward ? address(DAOrewardAccount).balance : reward;
if(_toMembers) {
if (!DAOrewardAccount.payOut(payable(dao.rewardAccount()), reward))
revert();
}
else {
if (!DAOrewardAccount.payOut(payable(dao), reward))
revert();
}
DAOpaidOut[msg.sender] += reward;
return true;
}
2019-09-16 12:33:43 +00:00
function getMyReward() public override returns (bool _success) {
return withdrawRewardFor(payable(msg.sender));
}
2019-09-16 12:33:43 +00:00
function withdrawRewardFor(address payable _account) noEther internal
override returns (bool _success) {
if ((balanceOf(_account) * rewardAccount.accumulatedInput()) / totalSupply < paidOut[_account])
revert();
uint reward =
(balanceOf(_account) * rewardAccount.accumulatedInput()) / totalSupply - paidOut[_account];
reward = address(rewardAccount).balance < reward ? address(rewardAccount).balance : reward;
if (!rewardAccount.payOut(_account, reward))
revert();
paidOut[_account] += reward;
return true;
}
2019-09-16 12:33:43 +00:00
function transfer(address _to, uint256 _value) public override returns (bool success) {
if (isFueled
&& block.timestamp > closingTime
&& !isBlocked(msg.sender)
&& _to != address(this)
&& transferPaidOut(msg.sender, _to, _value)
&& super.transfer(_to, _value)) {
return true;
} else {
revert();
}
}
2019-09-16 12:33:43 +00:00
function transferWithoutReward(address _to, uint256 _value) public override returns (bool success) {
if (!getMyReward())
revert();
return transfer(_to, _value);
}
2019-09-16 12:33:43 +00:00
function transferFrom(address _from, address _to, uint256 _value) public
override returns (bool success) {
if (isFueled
&& block.timestamp > closingTime
&& !isBlocked(_from)
&& _to != address(this)
&& transferPaidOut(_from, _to, _value)
&& super.transferFrom(_from, _to, _value)) {
return true;
} else {
revert();
}
}
function transferFromWithoutReward(
address payable _from,
address _to,
uint256 _value
2019-09-16 12:33:43 +00:00
) public override returns (bool success) {
if (!withdrawRewardFor(_from))
revert();
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])
revert();
paidOut[_from] -= transferPaidOut;
paidOut[_to] += transferPaidOut;
return true;
}
2019-09-16 12:33:43 +00:00
function changeProposalDeposit(uint _proposalDeposit) external override {
if (msg.sender != address(this) || _proposalDeposit > (actualBalance() + rewardToken[address(this)])
/ maxDepositDivisor) {
revert();
}
proposalDeposit = _proposalDeposit;
}
2019-09-16 12:33:43 +00:00
function changeAllowedRecipients(address _recipient, bool _allowed) external
override returns (bool _success) {
if (msg.sender != curator)
revert();
allowedRecipients[_recipient] = _allowed;
emit AllowedRecipientChanged(_recipient, _allowed);
return true;
}
function isRecipientAllowed(address _recipient) internal returns (bool _isAllowed) {
if (allowedRecipients[_recipient]
|| (_recipient == address(extraBalance)
// only allowed when at least the amount held in the
// extraBalance account has been spent from the DAO
&& totalRewardToken > extraBalance.accumulatedInput()))
return true;
else
return false;
}
function actualBalance() public view returns (uint _actualBalance) {
return address(this).balance - sumOfProposalDeposits;
}
function minQuorum(uint _value) internal view returns (uint _minQuorum) {
// minimum of 20% and maximum of 53.33%
return totalSupply / minQuorumDivisor +
(_value * totalSupply) / (3 * (actualBalance() + rewardToken[address(this)]));
}
2019-09-16 12:33:43 +00:00
function halveMinQuorum() public override returns (bool _success) {
// this can only be called after `quorumHalvingPeriod` has passed or at anytime after
// fueling by the curator with a delay of at least `minProposalDebatePeriod`
// between the calls
if ((lastTimeMinQuorumMet < (block.timestamp - quorumHalvingPeriod) || msg.sender == curator)
&& lastTimeMinQuorumMet < (block.timestamp - minProposalDebatePeriod)
&& block.timestamp >= closingTime
&& proposals.length > 1) {
lastTimeMinQuorumMet = block.timestamp;
minQuorumDivisor *= 2;
return true;
} else {
return false;
}
}
function createNewDAO(address payable _newCurator) internal returns (DAO _newDAO) {
emit NewCurator(_newCurator);
return daoCreator.createDAO(
_newCurator,
0,
0,
block.timestamp + splitExecutionPeriod,
name,
symbol,
decimals
);
}
2019-09-16 12:33:43 +00:00
function numberOfProposals() public view override returns (uint _numberOfProposals) {
// Don't count index 0. It's used by isBlocked() and exists from start
return proposals.length - 1;
}
2019-09-16 12:33:43 +00:00
function getNewDAOAddress(uint _proposalID) public override view returns (address _newDAO) {
return address(proposals[_proposalID].splitData[0].newDAO);
}
2019-09-16 12:33:43 +00:00
function isBlocked(address _account) internal override returns (bool) {
if (blocked[_account] == 0)
return false;
Proposal storage p = proposals[blocked[_account]];
if (block.timestamp > p.votingDeadline) {
blocked[_account] = 0;
return false;
} else {
return true;
}
}
2019-09-16 12:33:43 +00:00
function unblockMe() public override returns (bool) {
return isBlocked(msg.sender);
}
}
contract DAO_Creator {
function createDAO(
address payable _curator,
uint _proposalDeposit,
uint _minTokensToCreate,
uint _closingTime,
string memory _tokenName,
string memory _tokenSymbol,
uint8 _decimalPlaces
) public returns (DAO _newDAO) {
return new DAO(
_curator,
DAO_Creator(this),
_proposalDeposit,
_minTokensToCreate,
_closingTime,
msg.sender,
_tokenName,
_tokenSymbol,
_decimalPlaces
);
}
}