diff --git a/contracts/schemes/AuthorizedMintRep.sol b/contracts/schemes/AuthorizedMintRep.sol deleted file mode 100644 index 47d05c16..00000000 --- a/contracts/schemes/AuthorizedMintRep.sol +++ /dev/null @@ -1,63 +0,0 @@ -pragma solidity 0.5.13; - -import "openzeppelin-solidity/contracts/ownership/Ownable.sol"; -import "openzeppelin-solidity/contracts/math/SafeMath.sol"; -import "../controller/Controller.sol"; - -/** - * @title A scheme for reputation allocation by an authorized account - */ - -contract AuthorizedMintRep is Ownable { - using SafeMath for uint256; - - Avatar public avatar; - uint256 public activationStartTime; - uint256 public activationEndTime; - uint256 public repRewardLeft; - bool public limitRepReward; - - /** - * @dev initialize - * @param _avatar the avatar to mint reputation from - * @param _activationStartTime start time for allowing minting - * @param _activationEndTime end time for allowing minting - * @param _maxRepReward maximum reputation mintable by this scheme - */ - function initialize( - Avatar _avatar, - uint256 _activationStartTime, - uint256 _activationEndTime, - uint256 _maxRepReward - ) external onlyOwner { - require(avatar == Avatar(0), "can be called only one time"); - require(_avatar != Avatar(0), "avatar cannot be zero"); - require(_activationStartTime < _activationEndTime, "_activationStartTime < _activationEndTime"); - avatar = _avatar; - activationStartTime = _activationStartTime; - activationEndTime = _activationEndTime; - repRewardLeft = _maxRepReward; - limitRepReward = _maxRepReward != 0; - } - - /** - * @dev reputationMint function - * @param _beneficiary the beneficiary address to mint reputation for - * @param _amount the amount of reputation to mint the the beneficirary - */ - function reputationMint(address _beneficiary, uint256 _amount) external onlyOwner { - // solhint-disable-next-line not-rely-on-time - require(now >= activationStartTime, "Minting period did not start yet"); - // solhint-disable-next-line not-rely-on-time - require(now < activationEndTime, "Minting period ended."); - - if (limitRepReward) { - repRewardLeft = repRewardLeft.sub(_amount); - } - - require( - Controller(avatar.owner()).mintReputation(_amount, _beneficiary, address(avatar)), - "Minting reputation should succeed" - ); - } -} diff --git a/contracts/schemes/Competition.sol b/contracts/schemes/Competition.sol index 363e295f..819d183f 100644 --- a/contracts/schemes/Competition.sol +++ b/contracts/schemes/Competition.sol @@ -17,7 +17,8 @@ contract Competition { uint256 _suggestionsEndTime, uint256 _endTime, uint256 _maxNumberOfVotesPerVoter, - address payable _contributionRewardExt //address of the contract to redeem from. + address payable _contributionRewardExt, //address of the contract to redeem from. + address _admin ); event Redeem( @@ -30,7 +31,7 @@ contract Competition { bytes32 indexed _proposalId, uint256 indexed _suggestionId, string _descriptionHash, - address payable indexed _suggester + address payable indexed _beneficiary ); event NewVote( @@ -61,6 +62,7 @@ contract Competition { uint256 nativeTokenReward; uint256 externalTokenReward; uint256[] topSuggestions; + address admin; //mapping from suggestions totalVotes to the number of suggestions with the same totalVotes. mapping(uint256=>uint256) suggestionsPerVote; mapping(address=>uint256) votesPerVoter; @@ -69,7 +71,7 @@ contract Competition { struct Suggestion { uint256 totalVotes; bytes32 proposalId; - address payable suggester; + address payable beneficiary; mapping(address=>uint256) votes; } @@ -108,6 +110,10 @@ contract Competition { * _competitionParams[2] - _endTime competition end time * _competitionParams[3] - _maxNumberOfVotesPerVoter on how many suggestions a voter can vote * _competitionParams[4] - _suggestionsEndTime suggestion submition end time + * _proposerIsAdmin - + * true - proposer is an admin. + * false no admin. + * if admin is set, so only admin can suggest on this proposal. * @return proposalId the proposal id. */ function proposeCompetition( @@ -116,7 +122,8 @@ contract Competition { uint[3] calldata _rewards, IERC20 _externalToken, uint256[] calldata _rewardSplit, - uint256[5] calldata _competitionParams + uint256[5] calldata _competitionParams, + bool _proposerIsAdmin ) external returns(bytes32 proposalId) { @@ -141,12 +148,7 @@ contract Competition { } require(totalRewardSplit == 100, "total rewards split is not 100%"); proposalId = ContributionRewardExt(contributionRewardExt).proposeContributionReward( - _descriptionHash, - _reputationChange, - _rewards, - _externalToken, - contributionRewardExt, - msg.sender); + _descriptionHash, _reputationChange, _rewards, _externalToken, contributionRewardExt, msg.sender); proposals[proposalId].numberOfWinners = numberOfWinners; proposals[proposalId].rewardSplit = _rewardSplit; proposals[proposalId].startTime = startTime; @@ -158,7 +160,9 @@ contract Competition { proposals[proposalId].nativeTokenReward = _rewards[0]; proposals[proposalId].ethReward = _rewards[1]; proposals[proposalId].externalTokenReward = _rewards[2]; - + if (_proposerIsAdmin) { + proposals[proposalId].admin = msg.sender; + } emit NewCompetitionProposal( proposalId, numberOfWinners, @@ -168,7 +172,8 @@ contract Competition { proposals[proposalId].suggestionsEndTime, proposals[proposalId].endTime, proposals[proposalId].maxNumberOfVotesPerVoter, - contributionRewardExt + contributionRewardExt, + proposals[proposalId].admin ); } @@ -176,23 +181,34 @@ contract Competition { * @dev submit a competion suggestion * @param _proposalId the proposal id this suggestion is referring to. * @param _descriptionHash a descriptionHash of the suggestion. + * @param _beneficiary the beneficiary of this suggestion. * @return suggestionId the suggestionId. */ function suggest( bytes32 _proposalId, - string calldata _descriptionHash + string calldata _descriptionHash, + address payable _beneficiary ) external returns(uint256) { - // solhint-disable-next-line not-rely-on-time + if (proposals[_proposalId].admin != address(0)) { + require(proposals[_proposalId].admin == msg.sender, "only admin can suggest"); + } + // solhint-disable-next-line not-rely-on-time require(proposals[_proposalId].startTime <= now, "competition not started yet"); // solhint-disable-next-line not-rely-on-time require(proposals[_proposalId].suggestionsEndTime > now, "suggestions submition time is over"); suggestionsCounter = suggestionsCounter.add(1); suggestions[suggestionsCounter].proposalId = _proposalId; - suggestions[suggestionsCounter].suggester = msg.sender; - emit NewSuggestion(_proposalId, suggestionsCounter, _descriptionHash, msg.sender); + address payable beneficiary; + if (_beneficiary == address(0)) { + beneficiary = msg.sender; + } else { + beneficiary = _beneficiary; + } + suggestions[suggestionsCounter].beneficiary = beneficiary; + emit NewSuggestion(_proposalId, suggestionsCounter, _descriptionHash, beneficiary); return suggestionsCounter; } @@ -231,23 +247,6 @@ contract Competition { return true; } - /** - * @dev redeem a winning suggestion reward - * @param _suggestionId suggestionId - * @param _beneficiary - the reward beneficiary. - * this parameter is take into account only if the msg.sender is the suggestion's suggester, - * otherwise the _beneficiary param is ignored and the beneficiary is suggestion's suggester. - */ - function redeem(uint256 _suggestionId, address payable _beneficiary) external { - address payable beneficiary = suggestions[_suggestionId].suggester; - if ((msg.sender == suggestions[_suggestionId].suggester) && - (_beneficiary != address(0))) { - //only suggester can redeem to other address - beneficiary = _beneficiary; - } - _redeem(_suggestionId, beneficiary); - } - /** * @dev setSnapshotBlock set the block for the reputaion snapshot * @param _proposalId the proposal id @@ -270,7 +269,7 @@ contract Competition { require(proposals[_proposalId].endTime < now, "competition is still on"); uint256[] memory topSuggestions = proposals[_proposalId].topSuggestions; for (uint256 i; i < topSuggestions.length; i++) { - require(suggestions[topSuggestions[i]].suggester == address(0), "not all winning suggestions redeemed"); + require(suggestions[topSuggestions[i]].beneficiary == address(0), "not all winning suggestions redeemed"); } (, , , , , , @@ -291,6 +290,58 @@ contract Competition { _proposalId, address(avatar), nativeTokenRewardLeft); } + /** + * @dev redeem a winning suggestion reward + * @param _suggestionId suggestionId + */ + function redeem(uint256 _suggestionId) public { + bytes32 proposalId = suggestions[_suggestionId].proposalId; + Proposal storage proposal = proposals[proposalId]; + // solhint-disable-next-line not-rely-on-time + require(proposal.endTime < now, "competition is still on"); + require(suggestions[_suggestionId].beneficiary != address(0), + "suggestion was already redeemed"); + address payable beneficiary = suggestions[_suggestionId].beneficiary; + uint256 orderIndex = getOrderedIndexOfSuggestion(_suggestionId); + require(orderIndex < proposal.topSuggestions.length, "suggestion is not in winners list"); + suggestions[_suggestionId].beneficiary = address(0); + uint256 rewardPercentage = 0; + uint256 numberOfTieSuggestions = proposal.suggestionsPerVote[suggestions[_suggestionId].totalVotes]; + uint256 j; + //calc the reward percentage for this suggestion + for (j = orderIndex; j < (orderIndex+numberOfTieSuggestions) && j < proposal.numberOfWinners; j++) { + rewardPercentage = rewardPercentage.add(proposal.rewardSplit[j]); + } + rewardPercentage = rewardPercentage.div(numberOfTieSuggestions); + uint256 rewardPercentageLeft = 0; + if (proposal.topSuggestions.length < proposal.numberOfWinners) { + //if there are less winners than the proposal number of winners so divide the pre allocated + //left reward equally between the winners + for (j = proposal.topSuggestions.length; j < proposal.numberOfWinners; j++) { + rewardPercentageLeft = rewardPercentageLeft.add(proposal.rewardSplit[j]); + } + rewardPercentage = + rewardPercentage.add(rewardPercentageLeft.div(proposal.topSuggestions.length)); + } + uint256 amount; + amount = proposal.externalTokenReward.mul(rewardPercentage).div(100); + ContributionRewardExt(contributionRewardExt).redeemExternalTokenByRewarder( + proposalId, beneficiary, amount); + + amount = proposal.reputationReward.mul(rewardPercentage).div(100); + ContributionRewardExt(contributionRewardExt).redeemReputationByRewarder( + proposalId, beneficiary, amount); + + amount = proposal.ethReward.mul(rewardPercentage).div(100); + ContributionRewardExt(contributionRewardExt).redeemEtherByRewarder( + proposalId, beneficiary, amount); + + amount = proposal.nativeTokenReward.mul(rewardPercentage).div(100); + ContributionRewardExt(contributionRewardExt).redeemNativeTokenByRewarder( + proposalId, beneficiary, amount); + emit Redeem(proposalId, _suggestionId, rewardPercentage); + } + /** * @dev getOrderedIndexOfSuggestion return the index of specific suggestion in the winners list. * @param _suggestionId suggestion id @@ -348,56 +399,4 @@ contract Competition { } } - /** - * @dev redeem a winning suggestion reward - * @param _suggestionId suggestionId - * @param _beneficiary - the reward beneficiary - */ - function _redeem(uint256 _suggestionId, address payable _beneficiary) private { - bytes32 proposalId = suggestions[_suggestionId].proposalId; - Proposal storage proposal = proposals[proposalId]; - // solhint-disable-next-line not-rely-on-time - require(proposal.endTime < now, "competition is still on"); - require(suggestions[_suggestionId].suggester != address(0), - "suggestion was already redeemed"); - uint256 orderIndex = getOrderedIndexOfSuggestion(_suggestionId); - require(orderIndex < proposal.topSuggestions.length, "suggestion is not in winners list"); - suggestions[_suggestionId].suggester = address(0); - uint256 rewardPercentage = 0; - uint256 numberOfTieSuggestions = proposal.suggestionsPerVote[suggestions[_suggestionId].totalVotes]; - uint256 j; - //calc the reward percentage for this suggestion - for (j = orderIndex; j < (orderIndex+numberOfTieSuggestions) && j < proposal.numberOfWinners; j++) { - rewardPercentage = rewardPercentage.add(proposal.rewardSplit[j]); - } - rewardPercentage = rewardPercentage.div(numberOfTieSuggestions); - uint256 rewardPercentageLeft = 0; - if (proposal.topSuggestions.length < proposal.numberOfWinners) { - //if there are less winners than the proposal number of winners so divide the pre allocated - //left reward equally between the winners - for (j = proposal.topSuggestions.length; j < proposal.numberOfWinners; j++) { - rewardPercentageLeft = rewardPercentageLeft.add(proposal.rewardSplit[j]); - } - rewardPercentage = - rewardPercentage.add(rewardPercentageLeft.div(proposal.topSuggestions.length)); - } - uint256 amount; - amount = proposal.externalTokenReward.mul(rewardPercentage).div(100); - ContributionRewardExt(contributionRewardExt).redeemExternalTokenByRewarder( - proposalId, _beneficiary, amount); - - amount = proposal.reputationReward.mul(rewardPercentage).div(100); - ContributionRewardExt(contributionRewardExt).redeemReputationByRewarder( - proposalId, _beneficiary, amount); - - amount = proposal.ethReward.mul(rewardPercentage).div(100); - ContributionRewardExt(contributionRewardExt).redeemEtherByRewarder( - proposalId, _beneficiary, amount); - - amount = proposal.nativeTokenReward.mul(rewardPercentage).div(100); - ContributionRewardExt(contributionRewardExt).redeemNativeTokenByRewarder( - proposalId, _beneficiary, amount); - emit Redeem(proposalId, _suggestionId, rewardPercentage); - } - } diff --git a/contracts/schemes/ReputationAdmin.sol b/contracts/schemes/ReputationAdmin.sol new file mode 100644 index 00000000..f27a19b0 --- /dev/null +++ b/contracts/schemes/ReputationAdmin.sol @@ -0,0 +1,109 @@ +pragma solidity 0.5.13; + +import "openzeppelin-solidity/contracts/ownership/Ownable.sol"; +import "openzeppelin-solidity/contracts/math/SafeMath.sol"; +import "../controller/Controller.sol"; + +/** + * @title A scheme for reputation minting/burning by an authorized account + */ + +contract ReputationAdmin is Ownable { + using SafeMath for uint256; + + Avatar public avatar; + uint256 public activationStartTime; + uint256 public activationEndTime; + uint256 public repRewardLeft; + uint256 public limitRepReward; + + /** + * @dev initialize + * @param _avatar the avatar to mint reputation from + * @param _activationStartTime start time for allowing minting + * @param _activationEndTime end time for allowing minting + * @param _maxRepReward maximum reputation mintable by this scheme + */ + function initialize( + Avatar _avatar, + uint256 _activationStartTime, + uint256 _activationEndTime, + uint256 _maxRepReward + ) external onlyOwner { + require(avatar == Avatar(0), "can be called only one time"); + require(_avatar != Avatar(0), "avatar cannot be zero"); + require(_activationStartTime < _activationEndTime, "_activationStartTime < _activationEndTime"); + avatar = _avatar; + activationStartTime = _activationStartTime; + activationEndTime = _activationEndTime; + repRewardLeft = _maxRepReward; + limitRepReward = _maxRepReward; + } + + /** + * @dev reputationBurn function + * @param _beneficiaries the beneficiaries address to mint reputation from + * @param _amounts the amounts of reputation to mint for beneficiaries + */ + function reputationMint(address[] calldata _beneficiaries, uint256[] calldata _amounts) external onlyOwner { + require(_beneficiaries.length == _amounts.length, "Arrays length mismuch"); + for (uint256 i=0; i < _beneficiaries.length; i++) { + _reputationMint(_beneficiaries[i], _amounts[i]); + } + } + + /** + * @dev reputationBurn function + * @param _beneficiaries the beneficiaries address to burm reputation from + * @param _amounts the amounts of reputation to burn for beneficiaries + */ + function reputationBurn(address[] calldata _beneficiaries, uint256[] calldata _amounts) external onlyOwner { + require(_beneficiaries.length == _amounts.length, "Arrays length mismuch"); + for (uint256 i=0; i < _beneficiaries.length; i++) { + _reputationBurn(_beneficiaries[i], _amounts[i]); + } + } + + /** + * @dev reputationMint function + * @param _beneficiary the beneficiary address to mint reputation for + * @param _amount the amount of reputation to mint the the beneficirary + */ + function _reputationMint(address _beneficiary, uint256 _amount) private { + // solhint-disable-next-line not-rely-on-time + require(now >= activationStartTime, "Minting period did not start yet"); + // solhint-disable-next-line not-rely-on-time + require(now < activationEndTime, "Minting period ended."); + + if (limitRepReward > 0) { + repRewardLeft = repRewardLeft.sub(_amount); + } + + require( + Controller(avatar.owner()).mintReputation(_amount, _beneficiary, address(avatar)), + "Minting reputation should succeed" + ); + } + + /** + * @dev reputationBurn function + * @param _beneficiary the beneficiary address to burm reputation from + * @param _amount the amount of reputation to burn for a beneficirary + */ + function _reputationBurn(address _beneficiary, uint256 _amount) private { + // solhint-disable-next-line not-rely-on-time + require(now >= activationStartTime, "Burning period did not start yet"); + // solhint-disable-next-line not-rely-on-time + require(now < activationEndTime, "Burning period ended."); + + if (limitRepReward > 0) { + require(_amount <= limitRepReward.sub(repRewardLeft), "Cannot burn more than minted"); + repRewardLeft = repRewardLeft.add(_amount); + } + + require( + Controller(avatar.owner()).burnReputation(_amount, _beneficiary, address(avatar)), + "Burn reputation should succeed" + ); + } +} diff --git a/package-lock.json b/package-lock.json index 25fbcc5f..4945133e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@daostack/arc", - "version": "0.0.1-rc.38", + "version": "0.0.1-rc.39", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1971,9 +1971,9 @@ } }, "es-abstract": { - "version": "1.17.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.3.tgz", - "integrity": "sha512-AwiVPKf3sKGMoWtFw0J7Y4MTZ4Iek67k4COWOwHqS8B9TOZ71DCfcoBmdamy8Y6mj4MDz0+VNUpC2HKHFHA3pg==", + "version": "1.17.4", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz", + "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==", "dev": true, "requires": { "es-to-primitive": "^1.2.1", @@ -5894,9 +5894,9 @@ } }, "resolve": { - "version": "1.14.2", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.14.2.tgz", - "integrity": "sha512-EjlOBLBO1kxsUxsKjLt7TAECyKW6fOh1VRkykQkKGzcBbjjPIxBqGh0jf7GJ3k/f5mxMqW3htMD3WdTUVtW8HQ==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.0.tgz", + "integrity": "sha512-+hTmAldEGE80U2wJJDC1lebb5jWqvTYAfm3YZ1ckk1gBr0MnCqUKlwK1e+anaFljIl+F5tR5IoZcm4ZDA1zMQw==", "dev": true, "requires": { "path-parse": "^1.0.6" diff --git a/package.json b/package.json index e2c402be..179fbe60 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@daostack/arc", - "version": "0.0.1-rc.38", + "version": "0.0.1-rc.39", "description": "A platform for building DAOs", "files": [ "contracts/", diff --git a/test/competition.js b/test/competition.js index 08f254c4..45e2822c 100644 --- a/test/competition.js +++ b/test/competition.js @@ -65,6 +65,7 @@ const setup = async function (accounts,genesisProtocol = false,tokenAddress=0,se testSetup.competition = await Competition.new(); testSetup.competition.initialize(testSetup.contributionRewardExt.address); + testSetup.admin = accounts[0]; return testSetup; }; @@ -78,7 +79,8 @@ const proposeCompetition = async function( _votingStartTime = 600, _endTime = 1200, _maxNumberOfVotesPerVoter = 3, - _suggestionsEndTime = 1200 + _suggestionsEndTime = 1200, + _admin = helpers.NULL_ADDRESS ) { var block = await web3.eth.getBlock("latest"); @@ -86,6 +88,10 @@ const proposeCompetition = async function( _testSetup.votingStartTime = block.timestamp + _votingStartTime; _testSetup.endTime = block.timestamp + _endTime; _testSetup.suggestionsEndTime = block.timestamp + _suggestionsEndTime; + var sender = _testSetup.admin; + if (_admin !== helpers.NULL_ADDRESS) { + sender = _admin; + } var tx = await _testSetup.competition.proposeCompetition( _descriptionHash, _reputationChange, @@ -96,7 +102,9 @@ const proposeCompetition = async function( _testSetup.votingStartTime, _testSetup.endTime, _maxNumberOfVotesPerVoter, - _testSetup.suggestionsEndTime] + _testSetup.suggestionsEndTime], + (_admin !== helpers.NULL_ADDRESS), + {from:sender} ); var proposalId = await helpers.getValueFromLogs(tx, '_proposalId',1); @@ -114,6 +122,11 @@ const proposeCompetition = async function( assert.equal(tx.logs[0].args._maxNumberOfVotesPerVoter,_maxNumberOfVotesPerVoter); assert.equal(tx.logs[0].args._contributionRewardExt,_testSetup.contributionRewardExt.address); assert.equal(tx.logs[0].args._suggestionsEndTime,_testSetup.suggestionsEndTime); + if (_admin !== helpers.NULL_ADDRESS) { + assert.equal(tx.logs[0].args._admin,sender); + } else { + assert.equal(tx.logs[0].args._admin,helpers.NULL_ADDRESS); + } return proposalId; }; @@ -207,13 +220,33 @@ contract('Competition', accounts => { var testSetup = await setup(accounts); var proposalId = await proposeCompetition(testSetup); await helpers.increaseTime(20); - var tx = await testSetup.competition.suggest(proposalId,"suggestion"); + var tx = await testSetup.competition.suggest(proposalId,"suggestion",helpers.NULL_ADDRESS); assert.equal(tx.logs.length, 1); assert.equal(tx.logs[0].event, "NewSuggestion"); assert.equal(tx.logs[0].args._suggestionId,1); assert.equal(tx.logs[0].args._descriptionHash,"suggestion"); }); + it("suggest only admin", async function() { + var testSetup = await setup(accounts); + var admin = accounts[1]; + var proposalId = await proposeCompetition(testSetup, + "description-hash",10,[1,2,3],[50,25,15,10],10,600,1200,3,1200,admin); + await helpers.increaseTime(20); + try { + await testSetup.competition.suggest(proposalId,"suggestion",accounts[3]); + assert(false, 'only admin can suggest'); + } catch (ex) { + helpers.assertVMException(ex); + } + var tx = await testSetup.competition.suggest(proposalId,"suggestion",accounts[3],{from:admin}); + assert.equal(tx.logs.length, 1); + assert.equal(tx.logs[0].event, "NewSuggestion"); + assert.equal(tx.logs[0].args._suggestionId,1); + assert.equal(tx.logs[0].args._descriptionHash,"suggestion"); + assert.equal(tx.logs[0].args._beneficiary,accounts[3]); + }); + it("cannot suggest after suggestionEndTime", async function() { var descriptionHash = "description-hash"; var reputationChange = 10; @@ -235,12 +268,12 @@ contract('Competition', accounts => { maxNumberOfVotesPerVoter, 200);//suggestionEndTime await helpers.increaseTime(20);//increase time for suggestion - await testSetup.competition.suggest(proposalId,"suggestion"); + await testSetup.competition.suggest(proposalId,"suggestion",helpers.NULL_ADDRESS); //increase time after suggestion end time await helpers.increaseTime(250); try { - await testSetup.competition.suggest(proposalId,"suggestion"); + await testSetup.competition.suggest(proposalId,"suggestion",helpers.NULL_ADDRESS); assert(false, 'cannot suggest after suggestionEndTime'); } catch (ex) { helpers.assertVMException(ex); @@ -263,23 +296,23 @@ contract('Competition', accounts => { );//votingStartTime try { - await testSetup.competition.suggest(proposalId,"suggestion"); + await testSetup.competition.suggest(proposalId,"suggestion",helpers.NULL_ADDRESS); assert(false, 'cannot suggest before start time'); } catch (ex) { helpers.assertVMException(ex); } await helpers.increaseTime(20); - await testSetup.competition.suggest(proposalId,"suggestion"); + await testSetup.competition.suggest(proposalId,"suggestion",helpers.NULL_ADDRESS); }); it("cannot suggest after competition end", async function() { var testSetup = await setup(accounts); var proposalId = await proposeCompetition(testSetup);//votingStartTime await helpers.increaseTime(20); - await testSetup.competition.suggest(proposalId,"suggestion"); + await testSetup.competition.suggest(proposalId,"suggestion",helpers.NULL_ADDRESS); await helpers.increaseTime(1200+100); try { - await testSetup.competition.suggest(proposalId,"suggestion"); + await testSetup.competition.suggest(proposalId,"suggestion",helpers.NULL_ADDRESS); assert(false, 'cannot suggest after competition end'); } catch (ex) { helpers.assertVMException(ex); @@ -290,7 +323,7 @@ contract('Competition', accounts => { var testSetup = await setup(accounts); var proposalId = await proposeCompetition(testSetup); await helpers.increaseTime(20); - var tx = await testSetup.competition.suggest(proposalId,"suggestion"); + var tx = await testSetup.competition.suggest(proposalId,"suggestion",helpers.NULL_ADDRESS); var suggestionId = tx.logs[0].args._suggestionId; try { @@ -327,15 +360,15 @@ contract('Competition', accounts => { assert.equal(tx.logs[1].args._reputation,testSetup.reputationArray[0]); //first vote set the snapshotBlock - await testSetup.competition.suggest(proposalId,"suggestion"); + await testSetup.competition.suggest(proposalId,"suggestion",helpers.NULL_ADDRESS); await testSetup.competition.vote(2); proposal = await testSetup.competition.proposals(proposalId); assert.equal(proposal.snapshotBlock, tx.logs[0].blockNumber); //3rd suggestion - await testSetup.competition.suggest(proposalId,"suggestion"); + await testSetup.competition.suggest(proposalId,"suggestion",helpers.NULL_ADDRESS); //4th suggestion - await testSetup.competition.suggest(proposalId,"suggestion"); + await testSetup.competition.suggest(proposalId,"suggestion",helpers.NULL_ADDRESS); await testSetup.competition.vote(3); try { @@ -352,7 +385,7 @@ contract('Competition', accounts => { var testSetup = await setup(accounts); var proposalId = await proposeCompetition(testSetup); await helpers.increaseTime(20); - var tx = await testSetup.competition.suggest(proposalId,"suggestion"); + var tx = await testSetup.competition.suggest(proposalId,"suggestion",helpers.NULL_ADDRESS); var suggestionId = tx.logs[0].args._suggestionId; await helpers.increaseTime(650); await testSetup.competition.vote(suggestionId); @@ -368,7 +401,7 @@ contract('Competition', accounts => { await helpers.increaseTime(20); for (var i=0;i<20;i++) { //submit 20 suggestion - await testSetup.competition.suggest(proposalId,"suggestion"); + await testSetup.competition.suggest(proposalId,"suggestion",helpers.NULL_ADDRESS); } await helpers.increaseTime(650); await testSetup.competition.vote(10,{from:accounts[0]}); @@ -387,7 +420,7 @@ contract('Competition', accounts => { await helpers.increaseTime(20); for (var i=0;i<20;i++) { //submit 20 suggestion - await testSetup.competition.suggest(proposalId,"suggestion"); + await testSetup.competition.suggest(proposalId,"suggestion",helpers.NULL_ADDRESS); } await helpers.increaseTime(650); await testSetup.competition.vote(10,{from:accounts[1]}); @@ -418,9 +451,9 @@ contract('Competition', accounts => { await web3.eth.sendTransaction({from:accounts[0],to:testSetup.org.avatar.address, value:20}); var proposalId = await proposeCompetition(testSetup); await helpers.increaseTime(20); - await testSetup.competition.suggest(proposalId,"suggestion"); + await testSetup.competition.suggest(proposalId,"suggestion",helpers.NULL_ADDRESS); try { - await testSetup.competition.redeem(1,accounts[0]); + await testSetup.competition.redeem(1); assert(false, 'cannot redeem if no vote'); } catch (ex) { helpers.assertVMException(ex); @@ -431,13 +464,13 @@ contract('Competition', accounts => { await helpers.increaseTime(650); await testSetup.competition.vote(1,{from:accounts[1]}); try { - await testSetup.competition.redeem(1,accounts[0]); + await testSetup.competition.redeem(1); assert(false, 'cannot redeem if competion not ended yet'); } catch (ex) { helpers.assertVMException(ex); } await helpers.increaseTime(650); - var tx = await testSetup.competition.redeem(1,accounts[0]); + var tx = await testSetup.competition.redeem(1); assert.equal(tx.logs.length, 1); assert.equal(tx.logs[0].event, "Redeem"); assert.equal(tx.logs[0].args._proposalId,proposalId); @@ -492,12 +525,12 @@ contract('Competition', accounts => { var proposalId = await proposeCompetition(testSetup,"description-hash",1000,[1000,2000,3000]); await helpers.increaseTime(20); - await testSetup.competition.suggest(proposalId,"suggestion"); - await testSetup.competition.suggest(proposalId,"suggestion"); - await testSetup.competition.suggest(proposalId,"suggestion"); - await testSetup.competition.suggest(proposalId,"suggestion"); - await testSetup.competition.suggest(proposalId,"suggestion"); - await testSetup.competition.suggest(proposalId,"suggestion"); + await testSetup.competition.suggest(proposalId,"suggestion",helpers.NULL_ADDRESS); + await testSetup.competition.suggest(proposalId,"suggestion",helpers.NULL_ADDRESS); + await testSetup.competition.suggest(proposalId,"suggestion",helpers.NULL_ADDRESS); + await testSetup.competition.suggest(proposalId,"suggestion",helpers.NULL_ADDRESS); + await testSetup.competition.suggest(proposalId,"suggestion",helpers.NULL_ADDRESS); + await testSetup.competition.suggest(proposalId,"suggestion",helpers.NULL_ADDRESS); await testSetup.contributionRewardExtParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); await testSetup.contributionRewardExtParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[0]}); @@ -508,7 +541,7 @@ contract('Competition', accounts => { await testSetup.competition.vote(3,{from:accounts[2]}); await helpers.increaseTime(650); - var tx = await testSetup.competition.redeem(1,accounts[0]); + var tx = await testSetup.competition.redeem(1); assert.equal(tx.logs.length, 1); assert.equal(tx.logs[0].event, "Redeem"); assert.equal(tx.logs[0].args._proposalId,proposalId); @@ -553,7 +586,7 @@ contract('Competition', accounts => { assert.equal(events[0].args._beneficiary,accounts[0]); assert.equal(events[0].args._amount,(3000*18/100)); }); - tx = await testSetup.competition.redeem(2,accounts[0]); + tx = await testSetup.competition.redeem(2); assert.equal(tx.logs.length, 1); assert.equal(tx.logs[0].event, "Redeem"); assert.equal(tx.logs[0].args._proposalId,proposalId); @@ -566,7 +599,7 @@ contract('Competition', accounts => { helpers.assertVMException(ex); } - tx = await testSetup.competition.redeem(3,accounts[0]); + tx = await testSetup.competition.redeem(3); assert.equal(tx.logs.length, 1); assert.equal(tx.logs[0].event, "Redeem"); assert.equal(tx.logs[0].args._proposalId,proposalId); @@ -594,12 +627,12 @@ contract('Competition', accounts => { var proposalId = await proposeCompetition(testSetup,"description-hash",1000,[1000,2000,3000]); await helpers.increaseTime(20); - await testSetup.competition.suggest(proposalId,"suggestion"); - await testSetup.competition.suggest(proposalId,"suggestion"); - await testSetup.competition.suggest(proposalId,"suggestion"); - await testSetup.competition.suggest(proposalId,"suggestion"); - await testSetup.competition.suggest(proposalId,"suggestion"); - await testSetup.competition.suggest(proposalId,"suggestion"); + await testSetup.competition.suggest(proposalId,"suggestion",helpers.NULL_ADDRESS); + await testSetup.competition.suggest(proposalId,"suggestion",helpers.NULL_ADDRESS); + await testSetup.competition.suggest(proposalId,"suggestion",helpers.NULL_ADDRESS); + await testSetup.competition.suggest(proposalId,"suggestion",helpers.NULL_ADDRESS); + await testSetup.competition.suggest(proposalId,"suggestion",helpers.NULL_ADDRESS); + await testSetup.competition.suggest(proposalId,"suggestion",helpers.NULL_ADDRESS); await testSetup.contributionRewardExtParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); await testSetup.contributionRewardExtParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[0]}); @@ -612,19 +645,19 @@ contract('Competition', accounts => { await testSetup.competition.vote(5,{from:accounts[0]}); await testSetup.competition.vote(6,{from:accounts[0]}); await helpers.increaseTime(650); - var tx = await testSetup.competition.redeem(4,accounts[0]); + var tx = await testSetup.competition.redeem(4); assert.equal(tx.logs.length, 1); assert.equal(tx.logs[0].event, "Redeem"); assert.equal(tx.logs[0].args._proposalId,proposalId); assert.equal(tx.logs[0].args._rewardPercentage,3); - tx = await testSetup.competition.redeem(5,accounts[0]); + tx = await testSetup.competition.redeem(5); assert.equal(tx.logs.length, 1); assert.equal(tx.logs[0].event, "Redeem"); assert.equal(tx.logs[0].args._proposalId,proposalId); assert.equal(tx.logs[0].args._rewardPercentage,3); - tx = await testSetup.competition.redeem(6,accounts[0]); + tx = await testSetup.competition.redeem(6); assert.equal(tx.logs.length, 1); assert.equal(tx.logs[0].event, "Redeem"); assert.equal(tx.logs[0].args._proposalId,proposalId); @@ -638,8 +671,8 @@ contract('Competition', accounts => { var proposalId = await proposeCompetition(testSetup,"description-hash",1000,[1000,2000,3000],[50,30,10,10]); await helpers.increaseTime(20); - await testSetup.competition.suggest(proposalId,"suggestion"); - await testSetup.competition.suggest(proposalId,"suggestion"); + await testSetup.competition.suggest(proposalId,"suggestion",helpers.NULL_ADDRESS); + await testSetup.competition.suggest(proposalId,"suggestion",helpers.NULL_ADDRESS); await testSetup.contributionRewardExtParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[2]}); await testSetup.contributionRewardExtParams.votingMachine.absoluteVote.vote(proposalId,1,0,helpers.NULL_ADDRESS,{from:accounts[0]}); @@ -649,7 +682,7 @@ contract('Competition', accounts => { await testSetup.competition.vote(2,{from:accounts[0]}); await testSetup.competition.vote(2,{from:accounts[2]}); await helpers.increaseTime(650); - var tx = await testSetup.competition.redeem(2,accounts[0]); + var tx = await testSetup.competition.redeem(2); assert.equal(tx.logs.length, 1); assert.equal(tx.logs[0].event, "Redeem"); assert.equal(tx.logs[0].args._proposalId,proposalId); diff --git a/test/authorizedmintrep.js b/test/reputationadmin.js similarity index 71% rename from test/authorizedmintrep.js rename to test/reputationadmin.js index 1f00a1f2..5f278cf5 100644 --- a/test/authorizedmintrep.js +++ b/test/reputationadmin.js @@ -3,7 +3,7 @@ const DaoCreator = artifacts.require('./DaoCreator.sol'); const ControllerCreator = artifacts.require('./ControllerCreator.sol'); const DAOTracker = artifacts.require('./DAOTracker.sol'); const constants = require('./constants'); -var AuthorizedMintRep = artifacts.require('./AuthorizedMintRep.sol'); +var ReputationAdmin = artifacts.require('./ReputationAdmin.sol'); const setup = async function( accounts, @@ -34,9 +34,9 @@ const setup = async function( testSetup.activationEndTime = (await web3.eth.getBlock('latest')).timestamp + _activationEndTime; testSetup.maxRepReward = _maxRepReward; - testSetup.authorizedMintRep = await AuthorizedMintRep.new(); + testSetup.reputationAdmin = await ReputationAdmin.new(); if (_initialize === true) { - await testSetup.authorizedMintRep.initialize( + await testSetup.reputationAdmin.initialize( testSetup.org.avatar.address, testSetup.activationStartTime, testSetup.activationEndTime, @@ -48,7 +48,7 @@ const setup = async function( var permissions = '0x00000000'; await testSetup.daoCreator.setSchemes( testSetup.org.avatar.address, - [testSetup.authorizedMintRep.address], + [testSetup.reputationAdmin.address], [web3.utils.asciiToHex('0')], [permissions], 'metaData' @@ -57,30 +57,30 @@ const setup = async function( return testSetup; }; -contract('AuthorizedMintRep', accounts => { +contract('reputationAdmin', accounts => { it('initialize', async () => { let testSetup = await setup(accounts); assert.equal( - await testSetup.authorizedMintRep.repRewardLeft(), + await testSetup.reputationAdmin.repRewardLeft(), testSetup.maxRepReward ); assert.equal( - await testSetup.authorizedMintRep.activationStartTime(), + await testSetup.reputationAdmin.activationStartTime(), testSetup.activationStartTime ); assert.equal( - await testSetup.authorizedMintRep.activationEndTime(), + await testSetup.reputationAdmin.activationEndTime(), testSetup.activationEndTime ); - assert.equal(await testSetup.authorizedMintRep.owner(), accounts[0]); + assert.equal(await testSetup.reputationAdmin.owner(), accounts[0]); }); it('initialize _activationStartTime >= activationEndTime is not allowed', async () => { let testSetup = await setup(accounts); - let authorizedMintRep = await AuthorizedMintRep.new(); + let reputationAdmin = await ReputationAdmin.new(); try { - await authorizedMintRep.initialize( + await reputationAdmin.initialize( testSetup.org.avatar.address, testSetup.activationStartTime, testSetup.activationStartTime - 1, @@ -95,15 +95,36 @@ contract('AuthorizedMintRep', accounts => { it('mint reputation', async () => { let testSetup = await setup(accounts); - await testSetup.authorizedMintRep.reputationMint(accounts[2], 1); + await testSetup.reputationAdmin.reputationMint([accounts[2]],[1]); assert.equal(await testSetup.org.reputation.balanceOf(accounts[2]), 1); }); + it('burn reputation', async () => { + let testSetup = await setup(accounts); + await testSetup.reputationAdmin.reputationMint([accounts[2]],[1]); + + assert.equal(await testSetup.org.reputation.balanceOf(accounts[2]), 1); + await testSetup.reputationAdmin.reputationBurn([accounts[2]],[1]); + assert.equal(await testSetup.org.reputation.balanceOf(accounts[2]), 0); + + }); + + it('burn only if minted reputation', async () => { + let testSetup = await setup(accounts); + try { + await testSetup.reputationAdmin.reputationBurn([accounts[2]],[1]); + assert(false, 'burn only if minted reputation'); + } catch (error) { + helpers.assertVMException(error); + } + + }); + it('mint reputation by unauthorized account should fail', async () => { let testSetup = await setup(accounts); try { - await testSetup.authorizedMintRep.reputationMint(accounts[2], 1, { + await testSetup.reputationAdmin.reputationMint([accounts[2]], [1], { from: accounts[1] }); assert(false, 'mint reputation by unauthorized account should fail'); @@ -115,7 +136,7 @@ contract('AuthorizedMintRep', accounts => { it('mint without initialize should fail', async () => { let testSetup = await setup(accounts, 0, 3000, 100, false); try { - await testSetup.authorizedMintRep.reputationMint(accounts[2], 1); + await testSetup.reputationAdmin.reputationMint([accounts[2]], [1]); assert(false, 'mint without initialize should fail'); } catch (error) { helpers.assertVMException(error); @@ -125,7 +146,7 @@ contract('AuthorizedMintRep', accounts => { it('mint before _activationStartTime should fail', async () => { let testSetup = await setup(accounts, 2000, 3000, 100, true); try { - await testSetup.authorizedMintRep.reputationMint(accounts[2], 1); + await testSetup.reputationAdmin.reputationMint([accounts[2]],[1]); assert(false, 'mint before _activationStartTime should fail'); } catch (error) { helpers.assertVMException(error); @@ -136,7 +157,7 @@ contract('AuthorizedMintRep', accounts => { let testSetup = await setup(accounts); await helpers.increaseTime(3001); try { - await testSetup.authorizedMintRep.reputationMint(accounts[2], 1); + await testSetup.reputationAdmin.reputationMint([accounts[2]], [1]); assert(false, 'mint after _activationEndTime should revert'); } catch (error) { helpers.assertVMException(error); @@ -146,7 +167,7 @@ contract('AuthorizedMintRep', accounts => { it('mint more than _maxRepReward should fail', async () => { let testSetup = await setup(accounts); try { - await testSetup.authorizedMintRep.reputationMint(accounts[2], 101); + await testSetup.reputationAdmin.reputationMint([accounts[2]], [101]); assert(false, 'mint more than _maxRepReward should fail'); } catch (error) { helpers.assertVMException(error); @@ -155,7 +176,7 @@ contract('AuthorizedMintRep', accounts => { it('mint reputation with 0 _maxRepReward should allow any amount', async () => { let testSetup = await setup(accounts, 0, 3000, 0, true); - await testSetup.authorizedMintRep.reputationMint(accounts[2], 1000); + await testSetup.reputationAdmin.reputationMint([accounts[2]], [1000]); assert.equal(await testSetup.org.reputation.balanceOf(accounts[2]), 1000); }); @@ -163,7 +184,7 @@ contract('AuthorizedMintRep', accounts => { it('cannot initialize twice', async () => { let testSetup = await setup(accounts); try { - await testSetup.authorizedMintRep.initialize( + await testSetup.reputationAdmin.initialize( testSetup.org.avatar.address, testSetup.activationStartTime, testSetup.activationEndTime,