Skip to content

Commit

Permalink
Merge pull request #76 from Thetta/daico
Browse files Browse the repository at this point in the history
Daico template
  • Loading branch information
AnthonyAkentiev committed Dec 11, 2018
2 parents 195d6b2 + 2227dd6 commit 290a08b
Show file tree
Hide file tree
Showing 9 changed files with 1,291 additions and 50 deletions.
392 changes: 373 additions & 19 deletions contracts/Daico/Daico.sol

Large diffs are not rendered by default.

31 changes: 31 additions & 0 deletions contracts/Daico/DaicoOld.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
pragma solidity ^0.4.24;

// to enable Params passing to constructor and method
pragma experimental ABIEncoderV2;

import "@thetta/core/contracts/DaoClient.sol";
import "@thetta/core/contracts/IDaoBase.sol";
import "@thetta/core/contracts/tokens/StdDaoToken.sol";
import "./DaicoProject.sol";


contract DaicoOld is DaoClient {
mapping(uint => DaicoProject) public projects;
uint projectsCount;
address[] public investors;

bytes32 public NEXT_STAGE = keccak256("nextStage");

constructor(IDaoBase _daoBase, address[] _investors) DaoClient(_daoBase) {
investors = _investors;
}

function addNewProject(uint _stagesCount, uint _stageAmount) {
DaicoProject project = new DaicoProject(_stagesCount, _stageAmount, msg.sender, address(this));
projects[projectsCount] = project;
projectsCount++;
}

function addAmountToFund() public payable {}

}
35 changes: 35 additions & 0 deletions contracts/Daico/DaicoTestable.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
pragma solidity ^0.4.24;

import "zeppelin-solidity/contracts/token/ERC20/MintableToken.sol";

import "./Daico.sol";

contract DaicoTestable is Daico {

constructor(
address _daiTokenAddress,
address _projectTokenAddress,
address _projectOwnerAddress,
uint _tapsCount,
uint[] _tapAmounts,
uint[] _tapTimestampsFinishAt,
uint _minQuorumRate,
uint _minVoteRate,
uint _tokenHoldersCount
) public Daico(
_daiTokenAddress,
_projectTokenAddress,
_projectOwnerAddress,
_tapsCount,
_tapAmounts,
_tapTimestampsFinishAt,
_minQuorumRate,
_minVoteRate,
_tokenHoldersCount
) {}

function createVoting(uint _tapIndex, uint _quorumRate, uint _createdAt, uint _finishAt, VotingType _votingType) external {
_createVoting(_tapIndex, _quorumRate, _createdAt, _finishAt, _votingType);
}

}
6 changes: 3 additions & 3 deletions contracts/Daico/DaicoWithUnpackers.sol
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
pragma solidity ^0.4.24;

import "./Daico.sol";
import "./DaicoOld.sol";


contract DaicoWithUnpackers is Daico {
contract DaicoWithUnpackers is DaicoOld {

constructor(IDaoBase _daoBase, address[] _investors) Daico(_daoBase, _investors) {
constructor(IDaoBase _daoBase, address[] _investors) DaicoOld(_daoBase, _investors) {

}

Expand Down
240 changes: 240 additions & 0 deletions contracts/Daico/IDaico.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,240 @@
pragma solidity ^0.4.24;

import "zeppelin-solidity/contracts/ownership/Ownable.sol";
import "zeppelin-solidity/contracts/token/ERC20/ERC20.sol";

/**
* @title IDaico
* How it works:
* 1. Evercity member deploys current contract for some project.
* 2. Contract creates initial votings of type ReleaseTap for each tap.
* 3. Evercity member transfers DAI tokens to DAICO contract address.
*
* ==================
* Common scenarios:
* ==================
* # Successful voting for tap release
* 1. Token holders votes via 'vote()'.
* 2. Quorum reached with positive decision.
* 3. Project owner withdraws DAI tokens for accepted tap via 'withdrawTapPayment()'.
* # Quorum not reached for 'ReleaseTap' voting
* 1. Token holders votes via 'vote()'.
* 2. Quorum NOT reached.
* 3. Evercity member creates a new voting of type 'ReleaseTapDecreasedQuorum' via 'createVotingByOwner()'.
* 4. Quorum reached with positive decision.
* 5. Project owner withdraws DAI tokens for accepted tap via 'withdrawTapPayment()'.
*
* # Quorum reached but minVoteRate with positive decisions is not reached
* 1. Token holders votes via 'vote()'.
* 2. Quorum reached but minVoteRate with positive decisions is not reached
* 3. One of the investors creates a new voting of type 'ChangeRoadmap' via 'createVotingByInvestor()'.
* 4. Quorum reached with positive decision.
* 5. Project owner withdraws DAI tokens for accepted tap via 'withdrawTapPayment()'.
*
* # Voting strongly against tap release
* 1. Token holders votes via 'vote()'.
* 2. Quorum reached and more than minVoteRate token holders voted against tap release.
* 3. One of the investors creates a new voting of type 'TerminateProject' via 'createVotingByInvestor()'.
* 4. Quorum reached with positive decision.
* 5. Evercity member withdraws left DAI tokens via 'withdrawFunding()'.
*
* ===================
* All scenario steps:
* ===================
* # ReleaseTap/ReleaseTapDecreasedQuorum
* - voting of type ReleaseTap or ReleaseTapDecreasedQuorum finishes with Accept result => project owner withdraws DAI tokens via 'withdrawTapPayment()'
* - voting of type ReleaseTap or ReleaseTapDecreasedQuorum finishes with Decline result => one of the investors creates a voting of type TerminateProject via 'createVotingByInvestor()'
* - voting of type ReleaseTap or ReleaseTapDecreasedQuorum finishes with QuorumNotReached result => evercity member creates a voting of type ReleaseTapDecreasedQuorum via 'createVotingByOwner()'
* - voting of type ReleaseTap or ReleaseTapDecreasedQuorum finishes with NoDecision result => one of the investors creates a voting of type ChangeRoadmap via 'createVotingByInvestor()'
*
* # ChangeRoadmap/ChangeRoadmapDecreasedQuorum
* - voting of type ChangeRoadmap or ChangeRoadmapDecreasedQuorum finishes with Accept result => project owner withdraws DAI tokens via 'withdrawTapPayment()'
* - voting of type ChangeRoadmap or ChangeRoadmapDecreasedQuorum finishes with Decline result => one of the investors creates a voting of type TerminateProject via 'createVotingByInvestor()'
* - voting of type ChangeRoadmap or ChangeRoadmapDecreasedQuorum finishes with QuorumNotReached result => one of the investors creates a voting of type ChangeRoadmapDecreasedQuorum via 'createVotingByInvestor()'
* - voting of type ChangeRoadmap or ChangeRoadmapDecreasedQuorum finishes with NoDecision result => one of the investors creates a voting of type ChangeRoadmapDecreasedQuorum via 'createVotingByInvestor()'
*
* # TerminateProject/TerminateProjectDecreasedQuorum
* - voting of type TerminateProject or TerminateProjectDecreasedQuorum finishes with Accept result => evervity member withdraws DAI tokens via 'withdrawFunding()'
* - voting of type TerminateProject or TerminateProjectDecreasedQuorum finishes with Decline result => one of the investors creates a voting of type ChangeRoadmap via 'createVotingByInvestor()'
* - voting of type TerminateProject or TerminateProjectDecreasedQuorum finishes with QuorumNotReached result => one of the investors creates a voting of type TerminateProjectDecreasedQuorum via 'createVotingByInvestor()'
* - voting of type TerminateProject or TerminateProjectDecreasedQuorum finishes with NoDecision result => one of the investors creates a voting of type TerminateProjectDecreasedQuorum via 'createVotingByInvestor()'
*/
contract IDaico is Ownable {

ERC20 public daiToken;
ERC20 public projectToken;

address public projectOwner;

uint public minQuorumRate;
uint public minVoteRate;
uint public tapsCount;
uint public tokenHoldersCount;
uint[] public tapAmounts;
uint[] public tapTimestampsFinishAt;

enum VotingType { ReleaseTap, ReleaseTapDecreasedQuorum, ChangeRoadmap, TerminateProject }
enum VotingResult { Accept, Decline, QuorumNotReached, NoDecision }

mapping(uint => mapping(uint => uint)) public tapVotings;
mapping(uint => uint) public tapVotingsCount;

mapping(uint => Voting) public votings;
uint public votingsCount;

mapping(uint => TapPayment) public tapPayments;
uint public tapPaymentsCount;

struct TapPayment {
uint amount;
uint createdAt;
bool isWithdrawn;
}

struct Voting {
uint tapIndex;
uint yesVotesCount;
uint noVotesCount;
uint quorumRate;
uint createdAt;
uint finishAt;
VotingType votingType;
mapping(address => bool) voted;
}

/**
* @dev Contract constructor
* @param _daiTokenAddress address of the DAI token contract, project gets payments in DAI tokens
* @param _projectTokenAddress project token address, investors hold this token
* @param _projectOwnerAddress project owner address who can receive tap payments
* @param _tapsCount how many times project should get payments, NOTICE: we can get taps count from _tapAmounts.length but contract deployer can force so that _tapsCount != _tapAmounts.length
* @param _tapAmounts array of DAI token amounts in wei that describes how many tokens project gets per single stage
* @param _tapTimestampsFinishAt array of deadline timestamps, project should get payment before each deadline timestamp
* @param _minQuorumRate min quorum rate, 100 == 100%
* @param _minVoteRate min vote rate for proposal to be accepted/declined, 100 == 100%
* @param _tokenHoldersCount amount of token holders
*/
constructor(
address _daiTokenAddress,
address _projectTokenAddress,
address _projectOwnerAddress,
uint _tapsCount,
uint[] _tapAmounts,
uint[] _tapTimestampsFinishAt,
uint _minQuorumRate,
uint _minVoteRate,
uint _tokenHoldersCount
) public {}

/**
* Public methods
*/

/**
* @dev Returns voting result for voting.
* There are 4 voting results:
* - Accept: proposal accepted, majority of investors said 'yes'
* - Decline: proposal declined, majority of investors said 'no'
* - QuorumNotReached: not enough investors voted
* - NoDecision: no consensus among investors, ex: 50% of 'yes' vs 50% of 'no' votes
* @param _votingIndex voting index
* @return voting result
*/
function getVotingResult(uint _votingIndex) public view returns(VotingResult);

/**
* @dev Checks whether investor already voted in particular voting
* @param _votingIndex voting index
* @param _investorAddress investor address
* @return whether investor has already voted in particular voting
*/
function isInvestorVoted(uint _votingIndex, address _investorAddress) external view returns(bool);

/**
* @dev Checks whether project is terminated.
* Project is terminated when the last voting is of type TerminateProject/TerminateProjectDecreasedQuorum with Accept result.
* When project is terminated contract owner(evercity member) can withdraw DAI tokens via 'withdrawFunding()'.
* @return is project terminated
*/
function isProjectTerminated() public view returns(bool);

/**
* @dev Checks whether tap withdraw is accepted by investors for project owner
* @param _tapIndex tap index
* @return whether withdraw is accepted
*/
function isTapWithdrawAcceptedByInvestors(uint _tapIndex) public view returns(bool);

/**
* Investor methods
*/

/**
* @dev Creates a new voting by investor. Investors can create votings of 4 types: ChangeRoadmap, ChangeRoadmapDecreasedQuorum, TerminateProject, TerminateProjectDecreasedQuorum.
* @param _tapIndex tap index
* @param _votingType voting type
*/
function createVotingByInvestor(uint _tapIndex, VotingType _votingType) external;

/**
* @dev Vote method for token holder
* Preconditions:
* - Is token holder(has at least 1 project token)
* - Valid voting index
* - Is valid voting period
* - Investor hasn't voted earlier for this proposal
* - Project is not terminated
* @param _votingIndex voting index
* @param _isYes decision, yes or no
*/
function vote(uint _votingIndex, bool _isYes) external;

/**
* Owner / evercity member methods
*/

/**
* @dev Creates a new voting by owner. Owner can create votings only of type ReleaseTapDecreasedQuorum
* @param _tapIndex tap index
* @param _votingType voting type
*/
function createVotingByOwner(uint _tapIndex, VotingType _votingType) external;

/**
* @dev Withdraws all left DAI tokens to owner address
* Preconditions:
* - contract is terminated in case of successful TerminateProject voting
*/
function withdrawFunding() external;

/**
* Project owner methods
*/

/**
* @dev Withdraws tap payment by project owner
* Preconditions:
* - Valid tap index
* - Tap is not yet withdrawn
* - Tap withdrawal is accepted by investors
* @param _tapIndex tap index
* Result: DAI tokens for current tap are transfered to project owner address
*/
function withdrawTapPayment(uint _tapIndex) external;

/**
* Internal methods
*/

/**
* @dev Creates a new voting for tap
* @param _tapIndex tap index
* @param _quorumRate quorum rate
* @param _createdAt when voting was created timestamp
* @param _finishAt when voting should be finished timestamp
* @param _votingType voting type
*/
function _createVoting(uint _tapIndex, uint _quorumRate, uint _createdAt, uint _finishAt, VotingType _votingType) internal;

}
5 changes: 5 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"dotenv": "^6.0.0",
"ganache-cli": "^6.1.4",
"growl": "^1.10.0",
"moment": "^2.22.2",
"solidity-coverage": "^0.5.5",
"truffle": "^4.1.8",
"truffle-hdwallet-provider": "0.0.5",
Expand Down
Loading

0 comments on commit 290a08b

Please sign in to comment.