diff --git a/ethereum/contracts/DAO.sol b/ethereum/contracts/DAO.sol index fb0f9bf..0df6893 100644 --- a/ethereum/contracts/DAO.sol +++ b/ethereum/contracts/DAO.sol @@ -8,15 +8,20 @@ struct Stake { bool inFavor; uint256 amount; address sender; + uint256 tokenId; } struct ValidationPool { mapping(uint => Stake) stakes; uint stakeCount; + address author; + uint256 fee; uint duration; uint endTime; bool resolved; bool outcome; + uint256 tokenIdFor; + uint256 tokenIdAgainst; } struct StakeData { @@ -84,9 +89,15 @@ contract DAO is ERC721("Reputation", "REP"), ReputationHolder { } /// Accept fee to initiate a validation pool - function initiateValidationPool(uint duration) public payable { + /// TODO: Rather than accept author as a parameter, accept a reference to a forum post + function initiateValidationPool( + address author, + uint duration + ) public payable { uint poolIndex = validationPoolCount++; ValidationPool storage pool = validationPools[poolIndex]; + pool.author = author; + pool.fee = msg.value; pool.duration = duration; pool.endTime = block.timestamp + duration; // Because we need to stake part of the mited value for the pool an part against, @@ -95,10 +106,10 @@ contract DAO is ERC721("Reputation", "REP"), ReputationHolder { // Implementing this with adjustable parameters will require more advanced fixed point math. // TODO: Make minting ratio an adjustable parameter // TODO: Make stakeForAuthor an adjustable parameter - uint256 tokenIdFor = mint(msg.value / 2); - uint256 tokenIdAgainst = mint(msg.value / 2); - stake(pool, address(this), true, tokenIdFor); - stake(pool, address(this), false, tokenIdAgainst); + pool.tokenIdFor = mint(msg.value / 2); + pool.tokenIdAgainst = mint(msg.value / 2); + stake(pool, address(this), true, pool.tokenIdFor); + stake(pool, address(this), false, pool.tokenIdAgainst); emit ValidationPoolInitiated(poolIndex); } @@ -114,6 +125,7 @@ contract DAO is ERC721("Reputation", "REP"), ReputationHolder { _stake.sender = sender; _stake.inFavor = inFavor; _stake.amount = verifiedValueOf(sender, tokenId); + _stake.tokenId = tokenId; } /// Accept reputation stakes toward a validation pool @@ -133,13 +145,13 @@ contract DAO is ERC721("Reputation", "REP"), ReputationHolder { } /// Evaluate outcome of a validation pool - function evaluateOutcome(uint poolIndex) public returns (bool outcome) { + function evaluateOutcome(uint poolIndex) public returns (bool votePasses) { ValidationPool storage pool = validationPools[poolIndex]; require(block.timestamp >= pool.endTime); require(pool.resolved == false); uint256 amountFor; uint256 amountAgainst; - Stake memory _stake; + Stake storage _stake; for (uint i = 0; i < pool.stakeCount; i++) { _stake = pool.stakes[i]; if (_stake.inFavor) { @@ -153,9 +165,74 @@ contract DAO is ERC721("Reputation", "REP"), ReputationHolder { // A tie is resolved in favor of the validation pool. // This is especially important so that the DAO's first pool can pass, // when no reputation has yet been minted. - outcome = amountFor >= amountAgainst; + votePasses = amountFor >= amountAgainst; pool.resolved = true; - // Distribute reputation - // Distribute fee + // If the outcome is true, value of all stakes against the pool should be distributed among the stakes in favor. + // If the outcome is false, value of all stakes for the pool should be distributed among the stakes against. + uint256 amountFromWinners; + uint256 amountFromLosers; + // Collect the reputation from the losing stakes + for (uint i = 0; i < pool.stakeCount; i++) { + _stake = pool.stakes[i]; + if (votePasses && !_stake.inFavor) { + // Transfer value to the token that was minted in favor + amountFromLosers += _stake.amount; + transferValueFrom( + _stake.tokenId, + pool.tokenIdFor, + _stake.amount + ); + } else if (!votePasses && _stake.inFavor) { + // Transfer value to the token that was minted against + amountFromLosers += _stake.amount; + transferValueFrom( + _stake.tokenId, + pool.tokenIdAgainst, + _stake.amount + ); + } else if ( + votePasses && + _stake.inFavor && + _stake.tokenId != pool.tokenIdFor + ) { + // Tally the total value of winning stakes + amountFromWinners += _stake.amount; + } else if ( + !votePasses && + !_stake.inFavor && + _stake.tokenId != pool.tokenIdAgainst + ) { + // Tally the total value of winning stakes + amountFromWinners += _stake.amount; + } + } + // Distribute reputation from losing stakes to winning stakes + for (uint i = 0; i < pool.stakeCount; i++) { + _stake = pool.stakes[i]; + if ( + votePasses && + _stake.inFavor && + _stake.tokenId != pool.tokenIdFor + ) { + uint256 reward = (amountFromLosers * _stake.amount) / + amountFromWinners; + transferValueFrom(pool.tokenIdAgainst, _stake.tokenId, reward); + } else if ( + !votePasses && + !_stake.inFavor && + _stake.tokenId != pool.tokenIdAgainst + ) { + uint256 reward = (amountFromLosers * _stake.amount) / + amountFromWinners; + transferValueFrom(pool.tokenIdFor, _stake.tokenId, reward); + } + } + + // Distribute fee proportionatly among all reputation holders + for (uint tokenId = 0; tokenId < nextTokenId; tokenId++) { + uint256 share = (pool.fee * tokenValues[tokenId]) / totalValue; + address recipient = ownerOf(tokenId); + payable(recipient).transfer(share); + } } }