diff --git a/pkg/contracts/src/CVStrategy.sol b/pkg/contracts/src/CVStrategy.sol index cf60d5d3a..7080d2132 100644 --- a/pkg/contracts/src/CVStrategy.sol +++ b/pkg/contracts/src/CVStrategy.sol @@ -482,7 +482,7 @@ contract CVStrategy is BaseStrategy, IWithdrawMember { // @audit-ok they use 2^128 as the container for the result of the _pow function // uint256 atTWO_128 = _pow((decay << 128).div(D), t); - uint256 atTWO_128 = _pow((decay << 128) / D,t); + uint256 atTWO_128 = _pow((decay << 128) / D, t); // solium-disable-previous-line // conviction = (atTWO_128 * _lastConv + _oldAmount * D * (2^128 - atTWO_128) / (D - aD) + 2^127) / 2^128 // return (atTWO_128.mul(_lastConv).add(_oldAmount.mul(D).mul(TWO_128.sub(atTWO_128)).div(D - decay))).add(TWO_127) diff --git a/pkg/contracts/src/RegistryGardens.sol b/pkg/contracts/src/RegistryGardens.sol index 66de401f4..2f1868aa2 100644 --- a/pkg/contracts/src/RegistryGardens.sol +++ b/pkg/contracts/src/RegistryGardens.sol @@ -8,7 +8,6 @@ import {IRegistry} from "allo-v2-contracts/core/interfaces/IRegistry.sol"; import {ISafe} from "./interfaces/ISafe.sol"; contract RegistryGardens is ReentrancyGuard { - /*|--------------------------------------------|*/ /*| EVENTS |*/ /*|--------------------------------------------|*/ @@ -17,29 +16,30 @@ contract RegistryGardens is ReentrancyGuard { event MemberRegistered(address _member, uint256 _amountStaked); event MemberUnregistered(address _member, uint256 _amountReturned); event StakeAmountUpdated(address _member, uint256 _newAmount); - event CouncilMemberSet(address [] _councilMembers); + event CouncilMemberSet(address[] _councilMembers); event CouncilSafeSet(address _safe); event ProtocolFeeUpdated(uint256 _newFee); event AlloSet(address _allo); /*|--------------------------------------------|*/ /*| MODIFIERS |*/ /*|--------------------------------------------|*/ + modifier onlyCouncilMember() { - if(!isCouncilMember(msg.sender)) { + if (!isCouncilMember(msg.sender)) { revert UserNotInCouncil(); } _; } - modifier onlyRegistryMember(){ - if(!isMember(msg.sender)) { - revert UserNotInRegistry(); - } + modifier onlyRegistryMember() { + if (!isMember(msg.sender)) { + revert UserNotInRegistry(); + } _; } - modifier onlyGardenOwner(){ - if(msg.sender!=gardenOwner) { + modifier onlyGardenOwner() { + if (msg.sender != gardenOwner) { revert UserNotGardenOwner(); } _; @@ -54,7 +54,7 @@ contract RegistryGardens is ReentrancyGuard { error UserNotInRegistry(); error UserNotGardenOwner(); error StrategyExists(); - + /*|--------------------------------------------|*o /*| STRUCTS/ENUMS |*/ /*|--------------------------------------------|*/ @@ -107,24 +107,25 @@ contract RegistryGardens is ReentrancyGuard { function setCouncilMembers(address[] memory _members) public {} - function addStrategy(address _newStrategy) public onlyRegistryMember{ + function addStrategy(address _newStrategy) public onlyRegistryMember { if (enabledStrategies[_newStrategy]) { revert StrategyExists(); } enabledStrategies[_newStrategy] = true; emit StrategyAdded(_newStrategy); } + function revertZeroAddress(address _address) internal pure { - if(_address == address(0)) revert AddressCannotBeZero(); + if (_address == address(0)) revert AddressCannotBeZero(); } - function removeStrategy(address _strategy) public onlyCouncilMember{ + + function removeStrategy(address _strategy) public onlyCouncilMember { revertZeroAddress(_strategy); enabledStrategies[_strategy] = false; emit StrategyRemoved(_strategy); - } - function setAllo(address _allo) public { + function setAllo(address _allo) public { allo = IAllo(_allo); emit AlloSet(_allo); } @@ -135,7 +136,6 @@ contract RegistryGardens is ReentrancyGuard { emit CouncilSafeSet(_safe); } - function isMember(address _member) public view returns (bool _isMember) { Member memory newMember = addressToMemberInfo[_member]; return newMember.isRegistered; @@ -147,10 +147,10 @@ contract RegistryGardens is ReentrancyGuard { Member storage newMember = addressToMemberInfo[msg.sender]; require( //If fee percentage => minimumStakeAmount*protocolFee/100 - gardenToken.balanceOf(msg.sender) >= minimumStakeAmount+ protocolFee, + gardenToken.balanceOf(msg.sender) >= minimumStakeAmount + protocolFee, "[Registry]: Amount staked must be greater than minimum staked amount" ); - if(newMember.stakedAmount>= minimumStakeAmount){revert("already Staked");} + if (newMember.stakedAmount >= minimumStakeAmount) revert("already Staked"); //Check if already member newMember.isRegistered = true; newMember.stakedAmount = minimumStakeAmount; @@ -158,7 +158,8 @@ contract RegistryGardens is ReentrancyGuard { emit MemberRegistered(msg.sender, minimumStakeAmount); } //Check use of payable and msg.value - function modifyStakeAmount(uint256 newTotalAmount) public payable nonReentrant onlyRegistryMember{ + + function modifyStakeAmount(uint256 newTotalAmount) public payable nonReentrant onlyRegistryMember { Member storage member = addressToMemberInfo[msg.sender]; uint256 oldAmount = member.stakedAmount; member.stakedAmount = newTotalAmount; @@ -171,21 +172,22 @@ contract RegistryGardens is ReentrancyGuard { gardenToken.transferFrom(address(this), msg.sender, oldAmount - newTotalAmount); } - emit StakeAmountUpdated(msg.sender,newTotalAmount); + emit StakeAmountUpdated(msg.sender, newTotalAmount); } function getBasisStakedAmount() external view returns (uint256) { return minimumStakeAmount; } - function updateProtocolFee(uint256 _newProtocolFee) public{ - if(!isCouncilMember(msg.sender)) { + function updateProtocolFee(uint256 _newProtocolFee) public { + if (!isCouncilMember(msg.sender)) { revert("Must be in council safe"); } protocolFee = _newProtocolFee; emit ProtocolFeeUpdated(_newProtocolFee); } //function updateMinimumStake() + function isCouncilMember(address _member) public view returns (bool) { return councilMembers[_member]; } @@ -194,7 +196,7 @@ contract RegistryGardens is ReentrancyGuard { require(isMember(_member) || isCouncilMember(msg.sender), "[Registry]: Must be active member to unregister"); Member memory member = addressToMemberInfo[msg.sender]; delete addressToMemberInfo[msg.sender]; - gardenToken.transfer( msg.sender, member.stakedAmount); + gardenToken.transfer(msg.sender, member.stakedAmount); emit MemberUnregistered(msg.sender, member.stakedAmount); } } diff --git a/pkg/contracts/test/CVStrategyTest.t.sol b/pkg/contracts/test/CVStrategyTest.t.sol index 7728c8988..6cd988863 100644 --- a/pkg/contracts/test/CVStrategyTest.t.sol +++ b/pkg/contracts/test/CVStrategyTest.t.sol @@ -44,7 +44,6 @@ contract CVStrategyTest is Test, AlloSetup, RegistrySetupFull, Native, Errors, G uint256 internal constant TWO_128 = 2 ** 128; uint256 internal constant D = 10 ** 7; - function setUp() public { __RegistrySetupFull(); __AlloSetup(address(registry())); @@ -95,27 +94,9 @@ contract CVStrategyTest is Test, AlloSetup, RegistrySetupFull, Native, Errors, G // bytes data; - function testRevert_allocate_ProposalIdDuplicated() public { - ( /*IAllo.Pool memory pool*/ , uint256 poolId) = _createProposal(); - - /** - * ASSERTS - * - */ - startMeasuringGas("Support a Proposal"); - CVStrategy.ProposalSupport[] memory votes = new CVStrategy.ProposalSupport[](2); - // votes[0] = CVStrategy.ProposalSupport(1, 70); // 0 + 70 = 70% = 35 - votes[0] = CVStrategy.ProposalSupport(1, 80); // 0 + 70 = 70% = 35 - votes[1] = CVStrategy.ProposalSupport(1, 20); // 70 + 20 = 90% = 45 - // votes[2] = CVStrategy.ProposalSupport(1, -10); // 90 - 10 = 80% = 40 - // 35 + 45 + 40 = 120 - bytes memory data = abi.encode(votes); - // vm.expectRevert(CVStrategy.ProposalSupportDuplicated.selector); - vm.expectRevert(abi.encodeWithSelector(CVStrategy.ProposalSupportDuplicated.selector, 1, 0)); - allo().allocate(poolId, data); - stopMeasuringGas(); - } - + /** + * HELPERS FUNCTIONS + */ function _createProposal() public returns (IAllo.Pool memory pool, uint256 poolId) { startMeasuringGas("createProposal"); allo().addToCloneableStrategies(address(strategy)); @@ -156,73 +137,6 @@ contract CVStrategyTest is Test, AlloSetup, RegistrySetupFull, Native, Errors, G stopMeasuringGas(); } - // function test_proposalSupported() public { - // (IAllo.Pool memory pool, uint256 poolId) = _createProposal(); - - // /** - // * ASSERTS - // * - // */ - // startMeasuringGas("Support a Proposal"); - // CVStrategy.ProposalSupport[] memory votes = new CVStrategy.ProposalSupport[](1); - // votes[0] = CVStrategy.ProposalSupport(1, 80); // 0 + 70 = 70% = 35 - // bytes memory data = abi.encode(votes); - // allo().allocate(poolId, data); - // stopMeasuringGas(); - - // CVStrategy cv = CVStrategy(payable(address(pool.strategy))); - // assertEq(cv.getProposalVoterStake(1, address(this)), 40); // 80% of 50 = 40 - // assertEq(cv.getProposalStakedAmount(1), 40); // 80% of 50 = 40 - - // /** - // * ASSERTS - // * - // */ - // vm.startPrank(pool_admin()); - // CVStrategy.ProposalSupport[] memory votes2 = new CVStrategy.ProposalSupport[](1); - // votes2[0] = CVStrategy.ProposalSupport(1, 100); - // data = abi.encode(votes2); - // // vm.expectEmit(true, true, true, false); - // allo().allocate(poolId, data); - // vm.stopPrank(); - - // assertEq(cv.getProposalVoterStake(1, address(pool_admin())), 50); // 100% of 50 = 50 - // assertEq(cv.getProposalStakedAmount(1), 40 + 50); - - // /** - // * ASSERTS - // * - // */ - - // vm.warp(10 days); - - // ( - // address submitter, - // address beneficiary, - // address requestedToken, - // uint256 requestedAmount, - // uint256 stakedTokens, - // CVStrategy.ProposalType proposalType, - // CVStrategy.ProposalStatus proposalStatus, - // uint256 blockLast, - // uint256 convictionLast, - // uint256 agreementActionId, - // uint256 threshold - // ) = cv.getProposal(1); - - // // console.log("Proposal Status: %s", proposalStatus); - // // console.log("Proposal Type: %s", proposalType); - // // console.log("Requested Token: %s", requestedToken); - // // console.log("Requested Amount: %s", requestedAmount); - // console.log("Staked Tokens: %s", stakedTokens); - // console.log("Threshold: %s", threshold); - // // console.log("Agreement Action Id: %s", agreementActionId); - // console.log("Block Last: %s", blockLast); - // console.log("Conviction Last: %s", convictionLast); - // // console.log("Beneficiary: %s", beneficiary); - // // console.log("Submitter: %s", submitter); - // } - function _etherToFloat(uint256 _amount) internal pure returns (uint256) { return _amount / 10 ** 11; } @@ -248,15 +162,41 @@ contract CVStrategyTest is Test, AlloSetup, RegistrySetupFull, Native, Errors, G } } } + function _calculateConviction(uint256 _timePassed, uint256 _lastConv, uint256 _oldAmount, uint256 decay) public pure returns (uint256) { uint256 t = _timePassed; - uint256 atTWO_128 = _pow((decay << 128) / D,t); + uint256 atTWO_128 = _pow((decay << 128) / D, t); return (((atTWO_128 * _lastConv) + (_oldAmount * D * (TWO_128 - atTWO_128) / (D - decay))) + TWO_127) >> 128; } + + /** + * TESTS + */ + function testRevert_allocate_ProposalIdDuplicated() public { + ( /*IAllo.Pool memory pool*/ , uint256 poolId) = _createProposal(); + + /** + * ASSERTS + * + */ + startMeasuringGas("Support a Proposal"); + CVStrategy.ProposalSupport[] memory votes = new CVStrategy.ProposalSupport[](2); + // votes[0] = CVStrategy.ProposalSupport(1, 70); // 0 + 70 = 70% = 35 + votes[0] = CVStrategy.ProposalSupport(1, 80); // 0 + 70 = 70% = 35 + votes[1] = CVStrategy.ProposalSupport(1, 20); // 70 + 20 = 90% = 45 + // votes[2] = CVStrategy.ProposalSupport(1, -10); // 90 - 10 = 80% = 40 + // 35 + 45 + 40 = 120 + bytes memory data = abi.encode(votes); + // vm.expectRevert(CVStrategy.ProposalSupportDuplicated.selector); + vm.expectRevert(abi.encodeWithSelector(CVStrategy.ProposalSupportDuplicated.selector, 1, 0)); + allo().allocate(poolId, data); + stopMeasuringGas(); + } + function test_proposalSupported_2_times() public { (IAllo.Pool memory pool, uint256 poolId) = _createProposal(); @@ -313,7 +253,6 @@ contract CVStrategyTest is Test, AlloSetup, RegistrySetupFull, Native, Errors, G uint256 cv_cmp = _calculateConviction(10, 0, AMOUNT_STAKED, 0.9 ether / 10 ** 11); console.log("cv_cmp: %s", cv_cmp); assertEq(cv_amount, cv_cmp); - } function testRevert_allocate_removeSupport_wo_support_before_SUPPORT_UNDERFLOW() public { @@ -360,4 +299,76 @@ contract CVStrategyTest is Test, AlloSetup, RegistrySetupFull, Native, Errors, G assertEq(cv.getProposalVoterStake(1, address(this)), 50); // 100% of 50 = 50 assertEq(cv.getProposalStakedAmount(1), 50); } + + function test_1_proposalSupported() public { + (IAllo.Pool memory pool, uint256 poolId) = _createProposal(); + + /** + * ASSERTS + * + */ + startMeasuringGas("Support a Proposal"); + int256 SUPPORT_PCT = 80; + CVStrategy.ProposalSupport[] memory votes = new CVStrategy.ProposalSupport[](1); + votes[0] = CVStrategy.ProposalSupport(1, SUPPORT_PCT); // 0 + 70 = 70% = 35 + bytes memory data = abi.encode(votes); + allo().allocate(poolId, data); + stopMeasuringGas(); + + uint256 STAKED_AMOUNT = uint256(SUPPORT_PCT) * MINIMUM_STAKE / 100; + CVStrategy cv = CVStrategy(payable(address(pool.strategy))); + assertEq(cv.getProposalVoterStake(1, address(this)), STAKED_AMOUNT); // 80% of 50 = 40 + assertEq(cv.getProposalStakedAmount(1), STAKED_AMOUNT); // 80% of 50 = 40 + + /** + * ASSERTS + * + */ + vm.startPrank(pool_admin()); + CVStrategy.ProposalSupport[] memory votes2 = new CVStrategy.ProposalSupport[](1); + int256 SUPPORT_PCT2 = 100; + votes2[0] = CVStrategy.ProposalSupport(1, SUPPORT_PCT2); + data = abi.encode(votes2); + // vm.expectEmit(true, true, true, false); + allo().allocate(poolId, data); + vm.stopPrank(); + + uint256 STAKED_AMOUNT2 = uint256(SUPPORT_PCT2) * MINIMUM_STAKE / 100; + + assertEq(cv.getProposalVoterStake(1, address(pool_admin())), STAKED_AMOUNT2); // 100% of 50 = 50 + assertEq(cv.getProposalStakedAmount(1), STAKED_AMOUNT + STAKED_AMOUNT2); + + /** + * ASSERTS + * + */ + + vm.warp(10 days); + + ( + address submitter, + address beneficiary, + address requestedToken, + uint256 requestedAmount, + uint256 stakedTokens, + CVStrategy.ProposalType proposalType, + CVStrategy.ProposalStatus proposalStatus, + uint256 blockLast, + uint256 convictionLast, + uint256 agreementActionId, + uint256 threshold + ) = cv.getProposal(1); + + // console.log("Proposal Status: %s", proposalStatus); + // console.log("Proposal Type: %s", proposalType); + // console.log("Requested Token: %s", requestedToken); + console.log("Requested Amount: %s", requestedAmount); + console.log("Staked Tokens: %s", stakedTokens); + console.log("Threshold: %s", threshold); + // console.log("Agreement Action Id: %s", agreementActionId); + console.log("Block Last: %s", blockLast); + console.log("Conviction Last: %s", convictionLast); + // console.log("Beneficiary: %s", beneficiary); + // console.log("Submitter: %s", submitter); + } }