diff --git a/packages/protocol/test-sol/governance/voting/LockedGold.t.sol b/packages/protocol/test-sol/governance/voting/LockedGold.t.sol new file mode 100644 index 00000000000..ee279b693ca --- /dev/null +++ b/packages/protocol/test-sol/governance/voting/LockedGold.t.sol @@ -0,0 +1,2432 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.5.13; +pragma experimental ABIEncoderV2; + +import "celo-foundry/Test.sol"; +import "forge-std/console.sol"; + +import "../../../contracts/common/FixidityLib.sol"; +import "../../../contracts/common/Registry.sol"; +import "../../../contracts/common/Accounts.sol"; +import "../../../contracts/common/test/MockGoldToken.sol"; +import "../../../contracts/governance/LockedGold.sol"; +import "../../../contracts/governance/ReleaseGold.sol"; +import "../../../contracts/governance/Election.sol"; +import "../../../contracts/stability/test/MockStableToken.sol"; +import "../../../contracts/governance/test/MockElection.sol"; +import "../../../contracts/governance/test/MockGovernance.sol"; +import "../../../contracts/governance/test/MockValidators.sol"; + +contract LockedGoldTest is Test { + using FixidityLib for FixidityLib.Fraction; + + Registry registry; + Accounts accounts; + GoldToken goldToken; + MockStableToken stableToken; + MockElection election; + MockGovernance governance; + MockValidators validators; + LockedGold lockedGold; + ReleaseGold releaseGold; + + uint256 HOUR = 60 * 60; + uint256 DAY = 24 * HOUR; + uint256 unlockingPeriod = 3 * DAY; + + address randomAddress = actor("randomAddress"); + address caller = address(this); + + event UnlockingPeriodSet(uint256 period); + event GoldLocked(address indexed account, uint256 value); + event GoldUnlocked(address indexed account, uint256 value, uint256 available); + event GoldRelocked(address indexed account, uint256 value); + event GoldWithdrawn(address indexed account, uint256 value); + event SlasherWhitelistAdded(string indexed slasherIdentifier); + event SlasherWhitelistRemoved(string indexed slasherIdentifier); + event AccountSlashed( + address indexed slashed, + uint256 penalty, + address indexed reporter, + uint256 reward + ); + event CeloDelegated( + address indexed delegator, + address indexed delegatee, + uint256 percent, + uint256 amount + ); + event DelegatedCeloRevoked( + address indexed delegator, + address indexed delegatee, + uint256 percent, + uint256 amount + ); + event MaxDelegateesCountSet(uint256 value); + + function setUp() public { + address registryAddress = 0x000000000000000000000000000000000000ce10; + deployCodeTo("Registry.sol", abi.encode(false), registryAddress); + registry = Registry(registryAddress); + + goldToken = new MockGoldToken(); + accounts = new Accounts(true); + lockedGold = new LockedGold(true); + election = new MockElection(); + validators = new MockValidators(); + governance = new MockGovernance(); + stableToken = new MockStableToken(); + + registry.setAddressFor("Accounts", address(accounts)); + registry.setAddressFor("Election", address(election)); + registry.setAddressFor("GoldToken", address(goldToken)); + registry.setAddressFor("Governance", address(governance)); + registry.setAddressFor("LockedGold", address(lockedGold)); + registry.setAddressFor("Validators", address(validators)); + registry.setAddressFor("StableToken", address(stableToken)); + lockedGold.initialize(address(registry), unlockingPeriod); + accounts.createAccount(); + } + + function getParsedSignatureOfAddress(address _address, uint256 privateKey) + public + pure + returns (uint8, bytes32, bytes32) + { + bytes32 addressHash = keccak256(abi.encodePacked(_address)); + bytes32 prefixedHash = ECDSA.toEthSignedMessageHash(addressHash); + return vm.sign(privateKey, prefixedHash); + } + + function authorizeVoteSigner(address _delegator, uint256 _signerPK) public { + (uint8 vVoting, bytes32 rVoting, bytes32 sVoting) = getParsedSignatureOfAddress( + _delegator, + _signerPK + ); + + address signerAddress = vm.addr(_signerPK); + + vm.prank(_delegator); + accounts.authorizeVoteSigner(signerAddress, vVoting, rVoting, sVoting); + + vm.prank(signerAddress); + accounts.completeSignerAuthorization( + _delegator, + keccak256(abi.encodePacked("celo.org/core/vote")) + ); + } + + function createAndAssertDelegatorDelegateeSigners( + address _delegator, + address _delegatee, + uint256 _delegatorSignerPK, + uint256 _delegateeSignerPK + ) public { + if (_delegator != address(0)) { + authorizeVoteSigner(_delegator, _delegatorSignerPK); + assertFalse(_delegator == vm.addr(_delegatorSignerPK)); + assertEq(accounts.voteSignerToAccount(vm.addr(_delegatorSignerPK)), _delegator); + } + if (_delegatee != address(0)) { + authorizeVoteSigner(_delegatee, _delegateeSignerPK); + assertFalse(_delegatee == vm.addr(_delegateeSignerPK)); + assertEq(accounts.voteSignerToAccount(vm.addr(_delegateeSignerPK)), _delegatee); + } + } + + function assertDelegatorDelegateeAmounts( + address _delegator, + address delegatee, + uint256 percent, + uint256 amount + ) public { + (uint256 fraction, uint256 currentAmount) = lockedGold.getDelegatorDelegateeInfo( + _delegator, + delegatee + ); + assertEq(FixidityLib.wrap(fraction * 100).fromFixed(), percent, "fraction incorrect"); + assertEq(currentAmount, amount, "amount incorrect"); + } + + struct WhenVoteSignerStruct { + address delegator; + address delegator2; + address delegatee1; + address delegatee2; + uint256 delegatorSigner1PK; + uint256 delegateeSigner1PK; + uint256 delegatorSigner2PK; + uint256 delegateeSigner2PK; + bool lock; + } + + /** + * @notice Helper function to create assign vote signers to delegators and delegatees + */ + function helper_WhenVoteSigners(WhenVoteSignerStruct memory config) public { + if (config.lock) { + vm.prank(config.delegator); + lockedGold.lock.value(1000)(); + vm.prank(config.delegator2); + lockedGold.lock.value(1000)(); + } + + if (config.delegator != address(0)) { + createAndAssertDelegatorDelegateeSigners( + config.delegator, + config.delegatee1, + config.delegatorSigner1PK, + config.delegateeSigner1PK + ); + } + + if (config.delegator2 != address(0)) { + createAndAssertDelegatorDelegateeSigners( + config.delegator2, + config.delegatee2, + config.delegatorSigner2PK, + config.delegateeSigner2PK + ); + } + } + + function delegateCelo(address _delegator, address _delegatee, uint256 _percent) public { + vm.prank(_delegator); + lockedGold.delegateGovernanceVotes( + _delegatee, + FixidityLib.newFixedFraction(_percent, 100).unwrap() + ); + } + + function revokeDelegatedVotes(address _delegator, address _delegatee, uint256 _percent) public { + vm.prank(_delegator); + lockedGold.revokeDelegatedGovernanceVotes( + _delegatee, + FixidityLib.newFixedFraction(_percent, 100).unwrap() + ); + } + + function lockCelo(address celoOwner, uint256 value) public { + vm.prank(celoOwner); + lockedGold.lock.value(value)(); + } + +} + +contract LockedGoldInitialize is LockedGoldTest { + function setUp() public { + super.setUp(); + } + + function test_ShouldSetOwner() public { + assertEq(lockedGold.owner(), caller); + } + + function test_ShouldSetRegistryAddress() public { + assertEq(address(lockedGold.registry()), address(registry)); + } + + function test_ShouldSetUnlockingPeriod() public { + assertEq(lockedGold.unlockingPeriod(), unlockingPeriod); + } + + function test_ShouldRevertIfAlreadyInitialized() public { + vm.expectRevert("contract already initialized"); + lockedGold.initialize(address(registry), unlockingPeriod); + } +} + +contract LockedGoldSetRegistry is LockedGoldTest { + function setUp() public { + super.setUp(); + } + + function test_ShouldSetTheRegistryWhenCalledByTheOwner() public { + address newRegistry = actor("newAddress"); + lockedGold.setRegistry(newRegistry); + assertEq(address(lockedGold.registry()), newRegistry); + } + + function test_ShouldRevertWhenCalledByNonOwner() public { + vm.expectRevert("Ownable: caller is not the owner"); + vm.prank(randomAddress); + lockedGold.setRegistry(address(0)); + } +} + +contract LockedGoldSetUnlockingPeriod is LockedGoldTest { + function setUp() public { + super.setUp(); + } + + function test_ShouldSetTheUnlockingPeriod() public { + uint256 newUnlockingPeriod = 100; + lockedGold.setUnlockingPeriod(newUnlockingPeriod); + assertEq(lockedGold.unlockingPeriod(), newUnlockingPeriod); + } + + function test_ShouldEmitUnlockingPEriodSetEvent() public { + uint256 newUnlockingPeriod = 100; + vm.expectEmit(true, true, true, true); + emit UnlockingPeriodSet(newUnlockingPeriod); + lockedGold.setUnlockingPeriod(newUnlockingPeriod); + } + + function test_ShouldRevertWhenUnlockingPeriodIsUnchanged() public { + vm.expectRevert("Unlocking period not changed"); + lockedGold.setUnlockingPeriod(unlockingPeriod); + } + + function test_ShouldRevertWhenCalledByNonOwner() public { + vm.expectRevert("Ownable: caller is not the owner"); + vm.prank(randomAddress); + lockedGold.setUnlockingPeriod(100); + } +} + +contract LockedGoldLock is LockedGoldTest { + uint256 value = 1000; + function setUp() public { + super.setUp(); + } + + function test_ShouldIncreaseTheAccountsNonVotingLockedGoldBalance() public { + lockedGold.lock.value(value)(); + assertEq(lockedGold.getAccountNonvotingLockedGold(caller), value); + } + + function test_ShouldIncreaseTheAccountTOtalLockedGoldBalance() public { + lockedGold.lock.value(value)(); + assertEq(lockedGold.getAccountTotalLockedGold(caller), value); + } + + function test_ShouldIncreaseTheNonvotingLockedGoldBalance() public { + lockedGold.lock.value(value)(); + assertEq(lockedGold.getNonvotingLockedGold(), value); + } + + function test_ShouldIncreaseTheTotalLockedGoldBalance() public { + lockedGold.lock.value(value)(); + assertEq(lockedGold.getTotalLockedGold(), value); + } + + function test_ShouldEmitAGoldLockedEvent() public { + vm.expectEmit(true, true, true, true); + emit GoldLocked(caller, value); + lockedGold.lock.value(value)(); + } + + function test_ShouldRevertWhenAccountDoesNotExist() public { + vm.expectRevert("Must first register address with Account.createAccount"); + vm.prank(randomAddress); + lockedGold.lock(); + } + + function test_ShouldRevertWhenUserDoesntHaveEnoughBalance() public { + vm.expectRevert(); + vm.prank(randomAddress); + lockedGold.lock.value(1)(); + } +} + +contract LockedGoldUnlock is LockedGoldTest { + uint256 value = 1000; + uint256 availabilityTime = unlockingPeriod + block.timestamp; + + uint256 votingGold = 1; + uint256 nonVotingGold = value - votingGold; + + uint256 balanceRequirement = 10; + + function setUp() public { + super.setUp(); + lockedGold.lock.value(value)(); + } + + function test_ShouldAddAPendingWithdrawal_WhenAccountIsNotVotingInGovernance_WhenThereAreNoBalanceRequirements() + public + { + lockedGold.unlock(value); + (uint256 val, uint256 timestamp) = lockedGold.getPendingWithdrawal(caller, 0); + assertEq(val, value); + assertEq(timestamp, availabilityTime); + vm.expectRevert(); + lockedGold.getPendingWithdrawal(caller, 1); + } + + function test_ShouldAddPendingWithdrawals_WhenAccountIsNotVotingInGovernance_WhenThereAreNoBalanceRequirements() + public + { + lockedGold.unlock(value); + (uint256[] memory vals, uint256[] memory timestamps) = lockedGold.getPendingWithdrawals(caller); + assertEq(vals.length, 1); + assertEq(timestamps.length, 1); + assertEq(vals[0], value); + assertEq(timestamps[0], availabilityTime); + } + + function test_ShouldDecreaseTheACcountsNonVotingLockedGoldBalance_WhenAccountIsNotVotingInGovernance_WhenThereAreNoBalanceRequirements() + public + { + lockedGold.unlock(value); + assertEq(lockedGold.getAccountNonvotingLockedGold(caller), 0); + } + + function test_ShouldDecreaseTheAccountsTotalLockedGoldBalance_WhenAccountIsNotVotingInGovernance_WhenThereAreNoBalanceRequirements() + public + { + lockedGold.unlock(value); + assertEq(lockedGold.getAccountTotalLockedGold(caller), 0); + } + + function test_ShouldDecreaseTheNonVotingLockedGoldBalance_WhenAccountIsNotVotingInGovernance_WhenThereAreNoBalanceRequirements() + public + { + lockedGold.unlock(value); + assertEq(lockedGold.getNonvotingLockedGold(), 0); + } + + function test_ShouldDecreaseTheTotalLockedGoldBalance_WhenAccountIsNotVotingInGovernance_WhenThereAreNoBalanceRequirements() + public + { + lockedGold.unlock(value); + assertEq(lockedGold.getTotalLockedGold(), 0); + } + + function test_ShouldEmitGoldUnlockedEvent_WhenAccountIsNotVotingInGovernance_WhenThereAreNoBalanceRequirements() + public + { + vm.expectEmit(true, true, true, true); + emit GoldUnlocked(caller, value, availabilityTime); + lockedGold.unlock(value); + } + + function test_ShouldRevertWhenUnlockingGoldThatIsVotedWith_WhenThereAreNoBalanceRequirements() + public + { + governance.setVoting(caller); + governance.setTotalVotes(caller, votingGold); + + vm.expectRevert("Not enough unlockable celo. Celo is locked in voting."); + lockedGold.unlock(value); + } + + function test_ShouldRevertWhenUnlockingMoreThenLocked_WhenThereAreNoBalanceRequirements() public { + vm.expectRevert("SafeMath: subtraction overflow"); + lockedGold.unlock(value + 1); + } + + function test_ShouldAddAPendingWithdrawal_WhenTheAccountIsRequestingOnlyNonVotingGold_WhenThereAreNoBalanceRequirements() + public + { + governance.setVoting(caller); + governance.setTotalVotes(caller, votingGold); + + lockedGold.unlock(nonVotingGold); + (uint256 val, uint256 timestamp) = lockedGold.getPendingWithdrawal(caller, 0); + assertEq(val, nonVotingGold); + assertEq(timestamp, availabilityTime); + vm.expectRevert(); + lockedGold.getPendingWithdrawal(caller, 1); + } + + function test_ShouldAddPendingWithdrawals_WhenTheAccountIsRequestingOnlyNonVotingGold_WhenThereAreNoBalanceRequirements() + public + { + governance.setVoting(caller); + governance.setTotalVotes(caller, votingGold); + + lockedGold.unlock(nonVotingGold); + (uint256[] memory vals, uint256[] memory timestamps) = lockedGold.getPendingWithdrawals(caller); + assertEq(vals.length, 1); + assertEq(timestamps.length, 1); + assertEq(vals[0], nonVotingGold); + assertEq(timestamps[0], availabilityTime); + } + + function test_ShouldDecreaseTheACcountsNonVotingLockedGoldBalance_WhenTheAccountIsRequestingOnlyNonVotingGold_WhenThereAreNoBalanceRequirements() + public + { + governance.setVoting(caller); + governance.setTotalVotes(caller, votingGold); + + lockedGold.unlock(nonVotingGold); + assertEq(lockedGold.getAccountNonvotingLockedGold(caller), votingGold); + } + + function test_ShouldDecreaseTheAccountsTotalLockedGoldBalance_WhenTheAccountIsRequestingOnlyNonVotingGold_WhenThereAreNoBalanceRequirements() + public + { + governance.setVoting(caller); + governance.setTotalVotes(caller, votingGold); + + lockedGold.unlock(nonVotingGold); + assertEq(lockedGold.getAccountTotalLockedGold(caller), votingGold); + } + + function test_ShouldDecreaseTheNonVotingLockedGoldBalance_WhenTheAccountIsRequestingOnlyNonVotingGold_WhenThereAreNoBalanceRequirements() + public + { + governance.setVoting(caller); + governance.setTotalVotes(caller, votingGold); + + lockedGold.unlock(nonVotingGold); + assertEq(lockedGold.getNonvotingLockedGold(), votingGold); + } + + function test_ShouldDecreaseTheTotalLockedGoldBalance_WhenTheAccountIsRequestingOnlyNonVotingGold_WhenThereAreNoBalanceRequirements() + public + { + governance.setVoting(caller); + governance.setTotalVotes(caller, votingGold); + + lockedGold.unlock(nonVotingGold); + assertEq(lockedGold.getTotalLockedGold(), votingGold); + } + + function test_ShouldEmitGoldUnlockedEvent_WhenTheAccountIsRequestingOnlyNonVotingGold() public { + governance.setVoting(caller); + governance.setTotalVotes(caller, votingGold); + + vm.expectEmit(true, true, true, true); + emit GoldUnlocked(caller, nonVotingGold, availabilityTime); + lockedGold.unlock(nonVotingGold); + } + + function test_ShouldRevert_WhenTheCorrectTimeIsEarlierThanTheRequirementTime_WhenThereIsBalanceRequirement() + public + { + validators.setAccountLockedGoldRequirement(caller, balanceRequirement); + vm.expectRevert( + "Either account doesn't have enough locked Celo or locked Celo is being used for voting." + ); + lockedGold.unlock(value); + } + + function test_ShouldSucceed_WhenTheCorrectTimeIsEarlierThanTheRequirementTimeButRequestingCeloWithoutBalanceRequirement_WhenThereIsBalanceRequirement() + public + { + validators.setAccountLockedGoldRequirement(caller, balanceRequirement); + lockedGold.unlock(value - balanceRequirement); + } +} + +contract LockedGoldUnlockDelegation is LockedGoldTest { + uint256 value = 1000; + uint256 availabilityTime = unlockingPeriod + block.timestamp; + + uint256 percentageToDelegate = 50; + uint256 toUnlock = (value / 100) * (100 - percentageToDelegate); // 500 + uint256 originallyDelegatedAmount = (value / 100) * percentageToDelegate; // 500 + + address delegatee = actor("delegatee"); + + function setUp() public { + super.setUp(); + lockedGold.lock.value(value)(); + vm.prank(delegatee); + accounts.createAccount(); + lockedGold.delegateGovernanceVotes( + delegatee, + FixidityLib.newFixedFraction(percentageToDelegate, 100).unwrap() + ); + governance.setTotalVotes(delegatee, originallyDelegatedAmount); + lockedGold.unlock(toUnlock); + } + + function test_ShouldCorrectlyUnlockWhenGettingLessOrEqualToLockedAmount() public { + (uint256 val, uint256 timestamp) = lockedGold.getPendingWithdrawal(caller, 0); + assertEq(val, toUnlock); + assertEq(timestamp, availabilityTime); + } + + function test_ShouldCorrectlyUpdateDelegatedAmountForDelegatee() public { + (uint256 expected, uint256 real) = lockedGold.getDelegatorDelegateeExpectedAndRealAmount( + caller, + delegatee + ); + assertEq(expected, originallyDelegatedAmount / 2); + assertEq(real, toUnlock / 2); + } + + function test_ShouldDecreaseTotalDelegatedAmountForDelegatee() public { + assertEq(lockedGold.totalDelegatedCelo(delegatee), originallyDelegatedAmount / 2); + } + + function test_ShouldCallRemoveDelegatedVotesBecauseVotingForGovernance() public { + assertEq(governance.removeVotesCalledFor(delegatee), originallyDelegatedAmount / 2); + } + + function test_ShouldCorrectlyUpdateDelegatorDelegateeAmount() public { + (uint256 fraction, uint256 currentAmount) = lockedGold.getDelegatorDelegateeInfo( + caller, + delegatee + ); + assertEq(FixidityLib.wrap(fraction * 100).fromFixed(), percentageToDelegate); + assertEq(currentAmount, originallyDelegatedAmount / 2); + } + + function test_ShouldNotRemoveDelegateeFromQueue_WhenAllIsUnlocked() public { + governance.setTotalVotes(delegatee, originallyDelegatedAmount / 2); + lockedGold.unlock(toUnlock); + address[] memory delegatees = lockedGold.getDelegateesOfDelegator(caller); + assertEq(delegatees.length, 1); + assertEq(delegatees[0], delegatee); + } + + function test_ShouldCorrectlyUpdateDelegatorDelegateeAmount_WhenAllIsUnlocked() public { + governance.setTotalVotes(delegatee, originallyDelegatedAmount / 2); + lockedGold.unlock(toUnlock); + (uint256 fraction, uint256 currentAmount) = lockedGold.getDelegatorDelegateeInfo( + caller, + delegatee + ); + assertEq(FixidityLib.wrap(fraction * 100).fromFixed(), percentageToDelegate); + assertEq(currentAmount, 0); + } +} + +contract LockedGoldUnlockDelegation2Delegatees is LockedGoldTest { + uint256 value = 1000; + uint256 availabilityTime = unlockingPeriod + block.timestamp; + + uint256 percentageToDelegate = 50; + uint256 toUnlock = (value / 100) * (100 - percentageToDelegate) + 1; // 501 + uint256 originallyDelegatedAmount = (value / 100) * percentageToDelegate; // 500 + + address delegatee = actor("delegatee"); + address delegatee2 = actor("delegatee2"); + + function setUp() public { + super.setUp(); + lockedGold.lock.value(value)(); + vm.prank(delegatee); + accounts.createAccount(); + vm.prank(delegatee2); + accounts.createAccount(); + lockedGold.delegateGovernanceVotes( + delegatee, + FixidityLib.newFixedFraction(percentageToDelegate, 100).unwrap() + ); + lockedGold.delegateGovernanceVotes( + delegatee2, + FixidityLib.newFixedFraction(percentageToDelegate, 100).unwrap() + ); + governance.setTotalVotes(delegatee, originallyDelegatedAmount); + governance.setTotalVotes(delegatee2, originallyDelegatedAmount); + lockedGold.unlock(toUnlock); + } + + function test_ShouldCorrectlyUnlockWhenGettingLessOrEqualToLockedAmount() public { + (uint256 val, uint256 timestamp) = lockedGold.getPendingWithdrawal(caller, 0); + assertEq(val, toUnlock); + assertEq(timestamp, availabilityTime); + } + + function test_ShouldCorrectlyUpdateDelegatedAmountForDelegatee() public { + (uint256 expected, uint256 real) = lockedGold.getDelegatorDelegateeExpectedAndRealAmount( + caller, + delegatee + ); + assertEq(expected, originallyDelegatedAmount / 2 - 1); + assertEq(real, toUnlock / 2); + + (uint256 expected2, uint256 real2) = lockedGold.getDelegatorDelegateeExpectedAndRealAmount( + caller, + delegatee2 + ); + + assertEq(expected2, originallyDelegatedAmount / 2 - 1); + assertEq(real2, toUnlock / 2); + } + + function test_ShouldDecreaseTotalDelegatedAmountForDelegatee() public { + assertEq(lockedGold.totalDelegatedCelo(delegatee), originallyDelegatedAmount / 2); + assertEq(lockedGold.totalDelegatedCelo(delegatee2), originallyDelegatedAmount / 2); + } + + function test_ShouldCallRemoveDelegateVotesBecauseOfVotingForGovernance() public { + assertEq(governance.removeVotesCalledFor(delegatee), originallyDelegatedAmount / 2); + assertEq(governance.removeVotesCalledFor(delegatee2), originallyDelegatedAmount / 2); + } +} + +contract LockedGoldUnlockDelegationTo3Delegatees is LockedGoldTest { + uint256 value = 5; + uint256 availabilityTime = unlockingPeriod + block.timestamp; + + uint256 percentageToDelegate = 33; + uint256 toUnlock = 4; + + address delegatee = actor("delegatee"); + address delegatee2 = actor("delegatee2"); + address delegatee3 = actor("delegatee3"); + + function setUp() public { + super.setUp(); + lockedGold.lock.value(value)(); + vm.prank(delegatee); + accounts.createAccount(); + vm.prank(delegatee2); + accounts.createAccount(); + vm.prank(delegatee3); + accounts.createAccount(); + lockedGold.delegateGovernanceVotes( + delegatee, + FixidityLib.newFixedFraction(percentageToDelegate, 100).unwrap() + ); + lockedGold.delegateGovernanceVotes( + delegatee2, + FixidityLib.newFixedFraction(percentageToDelegate, 100).unwrap() + ); + lockedGold.delegateGovernanceVotes( + delegatee3, + FixidityLib.newFixedFraction(percentageToDelegate, 100).unwrap() + ); + + governance.setTotalVotes(delegatee, 1); + governance.setTotalVotes(delegatee2, 1); + governance.setTotalVotes(delegatee3, 1); + + governance.removeVotesWhenRevokingDelegatedVotes(delegatee, 9999); + governance.removeVotesWhenRevokingDelegatedVotes(delegatee2, 9999); + governance.removeVotesWhenRevokingDelegatedVotes(delegatee3, 9999); + } + + function test_ShouldDistributeCeloCorrectly() public { + (uint256 expected, uint256 real) = lockedGold.getDelegatorDelegateeExpectedAndRealAmount( + caller, + delegatee + ); + assertEq(expected, 1); + assertEq(real, 1); + + (uint256 expected2, uint256 real2) = lockedGold.getDelegatorDelegateeExpectedAndRealAmount( + caller, + delegatee2 + ); + + assertEq(expected2, 1); + assertEq(real2, 1); + + (uint256 expected3, uint256 real3) = lockedGold.getDelegatorDelegateeExpectedAndRealAmount( + caller, + delegatee3 + ); + + assertEq(expected3, 1); + assertEq(real3, 1); + + } + + function test_ShouldCorrectlyUnlockWhenGettingLessOrEqualToLockedAmount() public { + lockedGold.unlock(toUnlock); + (uint256 val, uint256 timestamp) = lockedGold.getPendingWithdrawal(caller, 0); + assertEq(val, toUnlock); + assertEq(timestamp, availabilityTime); + } + + function test_ShouldCorrectlyUpdateDelegatedAmountForDelegatee() public { + lockedGold.unlock(toUnlock); + (uint256 expected, uint256 real) = lockedGold.getDelegatorDelegateeExpectedAndRealAmount( + caller, + delegatee + ); + assertEq(expected, 0); + assertEq(real, 0); + + (uint256 expected2, uint256 real2) = lockedGold.getDelegatorDelegateeExpectedAndRealAmount( + caller, + delegatee2 + ); + + assertEq(expected2, 0); + assertEq(real2, 0); + + (uint256 expected3, uint256 real3) = lockedGold.getDelegatorDelegateeExpectedAndRealAmount( + caller, + delegatee3 + ); + + assertEq(expected3, 0); + assertEq(real3, 0); + } + + function test_ShouldDecreaseTotalDelegatedAmountForDelegatee() public { + lockedGold.unlock(toUnlock); + assertEq(lockedGold.totalDelegatedCelo(delegatee), 0); + assertEq(lockedGold.totalDelegatedCelo(delegatee2), 0); + assertEq(lockedGold.totalDelegatedCelo(delegatee3), 0); + } + + function test_ShouldCallRemoveDelegatedVotesBecauseOfVotingForGovernance() public { + lockedGold.unlock(toUnlock); + assertEq(governance.removeVotesCalledFor(delegatee), 0); + assertEq(governance.removeVotesCalledFor(delegatee2), 0); + assertEq(governance.removeVotesCalledFor(delegatee3), 0); + } + + function test_ShouldEmitDelegatedCeloRevokedEventForDelegatee1() public { + vm.expectEmit(true, true, true, true); + emit DelegatedCeloRevoked(caller, delegatee, 0, 1); + lockedGold.unlock(toUnlock); + } + + function test_ShouldEmitDelegatedCeloRevokedEventForDelegatee2() public { + vm.expectEmit(true, true, true, true); + emit DelegatedCeloRevoked(caller, delegatee2, 0, 1); + lockedGold.unlock(toUnlock); + } + + function test_ShouldEmitDelegatedCeloRevokedEventForDelegatee3() public { + vm.expectEmit(true, true, true, true); + emit DelegatedCeloRevoked(caller, delegatee3, 0, 1); + lockedGold.unlock(toUnlock); + } +} + +contract LockedGoldRelock is LockedGoldTest { + uint256 pendingWithdrawalValue = 100; + uint256 index = 0; + address delegatee = actor("delegatee"); + + function setUp() public { + super.setUp(); + lockedGold.lock.value(pendingWithdrawalValue)(); + } + + function helper_unlockRelockSameAmount() public { + lockedGold.unlock(pendingWithdrawalValue); + lockedGold.relock(index, pendingWithdrawalValue); + } + + function helper_unlockAndRelockLess() public { + lockedGold.unlock(pendingWithdrawalValue); + lockedGold.relock(index, pendingWithdrawalValue - 1); + } + + function test_ShouldIncreaseTheAccountsNonVotingLockedGoldBalance_WhenRelockingValueEqualToTheValueOfThePendingWithdrawal_WhenPendingWithdrawalExists() + public + { + helper_unlockRelockSameAmount(); + + assertEq(lockedGold.getAccountNonvotingLockedGold(caller), pendingWithdrawalValue); + } + + function test_ShouldIncreaseTheAccountsTotalLockedGoldBalance_WhenRelockingValueEqualToTheValueOfThePendingWithdrawal_WhenPendingWithdrawalExists() + public + { + helper_unlockRelockSameAmount(); + + assertEq(lockedGold.getAccountTotalLockedGold(caller), pendingWithdrawalValue); + } + + function test_ShouldIncreaseTheNonVotingLockedGoldBalance_WhenRelockingValueEqualToTheValueOfThePendingWithdrawal_WhenPendingWithdrawalExists() + public + { + helper_unlockRelockSameAmount(); + + assertEq(lockedGold.getNonvotingLockedGold(), pendingWithdrawalValue); + } + + function test_ShouldIncreaseTheTotalLockedGoldBalance_WhenRelockingValueEqualToTheValueOfThePendingWithdrawal_WhenPendingWithdrawalExists() + public + { + helper_unlockRelockSameAmount(); + + assertEq(lockedGold.getTotalLockedGold(), pendingWithdrawalValue); + } + + function test_ShouldEmitGoldRelockedEvent_WhenRelockingValueEqualToTheValueOfThePendingWithdrawal_WhenPendingWithdrawalExists() + public + { + lockedGold.unlock(pendingWithdrawalValue); + vm.expectEmit(true, true, true, true); + emit GoldRelocked(caller, pendingWithdrawalValue); + lockedGold.relock(index, pendingWithdrawalValue); + } + + function test_ShouldRemoveThePendingWithdrawal_WhenRelockingValueEqualToTheValueOfThePendingWithdrawal_WhenPendingWithdrawalExists() + public + { + helper_unlockRelockSameAmount(); + + (uint256[] memory vals, uint256[] memory timestamps) = lockedGold.getPendingWithdrawals(caller); + assertEq(vals.length, 0); + assertEq(timestamps.length, 0); + } + + function test_ShouldIncreaseTheAccountsNonVotingLockedGoldBalance_WhenRelockingValueLessThanValueOfThePendingWithdrawal_WhenPendingWithdrawalExists() + public + { + helper_unlockAndRelockLess(); + + assertEq(lockedGold.getAccountNonvotingLockedGold(caller), pendingWithdrawalValue - 1); + } + + function test_ShouldIncreaseTheAccountsTotalLockedGoldBalance_WhenRelockingValueLessThanTheValueOfThePendingWithdrawal_WhenPendingWithdrawalExists() + public + { + helper_unlockAndRelockLess(); + + assertEq(lockedGold.getAccountTotalLockedGold(caller), pendingWithdrawalValue - 1); + } + + function test_ShouldIncreaseTheNonVotingLockedGoldBalance_WhenRelockingValueLessThanTheValueOfThePendingWithdrawal_WhenPendingWithdrawalExists() + public + { + helper_unlockAndRelockLess(); + + assertEq(lockedGold.getNonvotingLockedGold(), pendingWithdrawalValue - 1); + } + + function test_ShouldIncreaseTheTotalLockedGoldBalance_WhenRelockingValueLessThanTheValueOfThePendingWithdrawal_WhenPendingWithdrawalExists() + public + { + helper_unlockAndRelockLess(); + + assertEq(lockedGold.getTotalLockedGold(), pendingWithdrawalValue - 1); + } + + function test_ShouldEmitGoldRelockedEvent_WhenRelockingValueLessThanTheValueOfThePendingWithdrawal_WhenPendingWithdrawalExists() + public + { + lockedGold.unlock(pendingWithdrawalValue); + vm.expectEmit(true, true, true, true); + emit GoldRelocked(caller, pendingWithdrawalValue - 1); + lockedGold.relock(index, pendingWithdrawalValue - 1); + } + + function test_ShouldRemoveThePendingWithdrawal_WhenRelockingValueLessThanTheValueOfThePendingWithdrawal_WhenPendingWithdrawalExists() + public + { + lockedGold.unlock(pendingWithdrawalValue); + lockedGold.relock(index, pendingWithdrawalValue - 1); + + (uint256[] memory vals, uint256[] memory timestamps) = lockedGold.getPendingWithdrawals(caller); + assertEq(vals.length, 1); + assertEq(timestamps.length, 1); + assertEq(vals[0], 1); + } + + function test_ShouldRevert_WhenRelockingValueGreaterThanTheValueOfThePendingWithdrawal_WhenPendingWithdrawalExists() + public + { + lockedGold.unlock(pendingWithdrawalValue); + vm.expectRevert("Requested value larger than pending value"); + lockedGold.relock(index, pendingWithdrawalValue + 1); + } + + function test_ShouldUpdateDelegatorDelegateeAmount_WhenDelegating() public { + vm.prank(delegatee); + accounts.createAccount(); + lockedGold.delegateGovernanceVotes(delegatee, FixidityLib.newFixedFraction(1, 1).unwrap()); + lockedGold.unlock(pendingWithdrawalValue / 2); + + (uint256 expected, uint256 real) = lockedGold.getDelegatorDelegateeExpectedAndRealAmount( + caller, + delegatee + ); + + assertEq(expected, pendingWithdrawalValue / 2); + assertEq(real, pendingWithdrawalValue / 2); + + lockedGold.relock(index, pendingWithdrawalValue / 2); + + (uint256 expected2, uint256 real2) = lockedGold.getDelegatorDelegateeExpectedAndRealAmount( + caller, + delegatee + ); + + assertEq(expected2, pendingWithdrawalValue); + assertEq(real2, pendingWithdrawalValue); + } + + function test_ShouldRevert_WhenPendingWithdrawalDoesNotExists() public { + vm.expectRevert("Bad pending withdrawal index"); + lockedGold.relock(0, pendingWithdrawalValue); + } +} + +contract LockedGoldWithdraw is LockedGoldTest { + uint256 value = 1000; + uint256 index = 0; + + function setUp() public { + super.setUp(); + lockedGold.lock.value(value)(); + } + + function test_ShouldRemoveThePendingWithdrawal_WhenItIsAFterTheAvailabilityTime() public { + lockedGold.unlock(value); + vm.warp(block.timestamp + unlockingPeriod + 1); + lockedGold.withdraw(index); + (uint256[] memory vals, uint256[] memory timestamps) = lockedGold.getPendingWithdrawals(caller); + assertEq(vals.length, 0); + assertEq(timestamps.length, 0); + } + + function test_ShouldEmitGoldWithdrawnEvent_WhenItIsAfterTheAvailabilityTime() public { + lockedGold.unlock(value); + vm.warp(block.timestamp + unlockingPeriod + 1); + vm.expectEmit(true, true, true, true); + emit GoldWithdrawn(caller, value); + lockedGold.withdraw(index); + } + + function test_ShouldRevert_WhenItIsBeforeTheAvailabilityTime() public { + lockedGold.unlock(value); + vm.expectRevert("Pending withdrawal not available"); + lockedGold.withdraw(index); + } + + function test_ShouldRevert_WhenPendingWithdrawalDoesNotExist() public { + vm.expectRevert("Bad pending withdrawal index"); + lockedGold.withdraw(index); + } + + function() external payable {} +} + +contract LockedGoldAddSlasher is LockedGoldTest { + string slasherName = "DowntimeSlasher"; + address downtimeSlasher = actor(slasherName); + + function setUp() public { + super.setUp(); + registry.setAddressFor(slasherName, downtimeSlasher); + } + + function test_ShouldBeAbleToAddSlasherToWhitelist() public { + lockedGold.addSlasher(slasherName); + bytes32[] memory slashers = lockedGold.getSlashingWhitelist(); + assertEq(slashers[0], keccak256(abi.encodePacked(slasherName))); + } + + function test_ShouldBeCallableOnlyByOwner() public { + vm.expectRevert("Ownable: caller is not the owner"); + vm.prank(randomAddress); + lockedGold.addSlasher(slasherName); + } + + function test_ShouldNotAllowToAddSlasherTwice() public { + lockedGold.addSlasher(slasherName); + vm.expectRevert("Cannot add slasher ID twice."); + lockedGold.addSlasher(slasherName); + } +} + +contract LockedGoldRemoveSlasher is LockedGoldTest { + string slasherName = "DowntimeSlasher"; + string governanceSlasherName = "GovernanceSlasher"; + address downtimeSlasher = actor(slasherName); + address governanceSlasher = actor(governanceSlasherName); + + function setUp() public { + super.setUp(); + registry.setAddressFor(slasherName, downtimeSlasher); + registry.setAddressFor(governanceSlasherName, governanceSlasher); + lockedGold.addSlasher(slasherName); + } + + function test_ShouldRemoveItemFromWhitelist() public { + lockedGold.removeSlasher(slasherName, 0); + bytes32[] memory slashers = lockedGold.getSlashingWhitelist(); + assertEq(slashers.length, 0); + } + + function test_ShouldBeCallableOnlyByTheOwner() public { + vm.expectRevert("Ownable: caller is not the owner"); + vm.prank(randomAddress); + lockedGold.removeSlasher(slasherName, 0); + } + + function test_ShouldRevertWhenIndexTooLarge() public { + vm.expectRevert("Provided index exceeds whitelist bounds."); + lockedGold.removeSlasher(slasherName, 1); + } + + function test_ShouldRevertWhenKeyDoesNotExist() public { + vm.expectRevert("Cannot remove slasher ID not yet added."); + lockedGold.removeSlasher(governanceSlasherName, 0); + } + + function test_ShouldRevertWhenIndexAndKeyHaveMismatch() public { + lockedGold.addSlasher(governanceSlasherName); + vm.expectRevert("Index doesn't match identifier"); + lockedGold.removeSlasher(slasherName, 1); + } +} + +contract LockedGoldSlash is LockedGoldTest { + string slasherName = "DowntimeSlasher"; + uint256 value = 1000; + address group = actor("group"); + address groupMember = actor("groupMember"); + address reporter = actor("reporter"); + address downtimeSlasher = actor(slasherName); + address delegatee = actor("delegatee"); + + Election electionSlashTest; + + function setUp() public { + super.setUp(); + electionSlashTest = new Election(true); + registry.setAddressFor("Election", address(electionSlashTest)); + electionSlashTest.initialize( + address(registry), + 4, + 6, + 3, + FixidityLib.newFixedFraction(1, 100).unwrap() + ); + + address[] memory members = new address[](1); + members[0] = groupMember; + + validators.setMembers(group, members); + registry.setAddressFor("Validators", caller); + electionSlashTest.markGroupEligible(group, address(0), address(0)); + registry.setAddressFor("Validators", address(validators)); + validators.setNumRegisteredValidators(1); + + lockedGold.lock.value(value)(); + registry.setAddressFor(slasherName, downtimeSlasher); + lockedGold.addSlasher(slasherName); + + vm.prank(reporter); + accounts.createAccount(); + } + + function helper_WhenAccountIsSlashedForAllOfItsLockedGold(uint256 penalty, uint256 reward) + public + { + address[] memory lessers = new address[](1); + lessers[0] = address(0); + address[] memory greaters = new address[](1); + greaters[0] = address(0); + + uint256[] memory indices = new uint256[](1); + indices[0] = 0; + + vm.prank(downtimeSlasher); + lockedGold.slash(caller, penalty, reporter, reward, lessers, greaters, indices); + } + + function test_ShouldReduceAccountsLockedGoldBalance_WhenAccountIsSlashedForAllOfItsLockedGold() + public + { + uint256 penalty = value; + uint256 reward = value / 2; + helper_WhenAccountIsSlashedForAllOfItsLockedGold(penalty, reward); + + assertEq(lockedGold.getAccountNonvotingLockedGold(caller), value - penalty); + assertEq(lockedGold.getAccountTotalLockedGold(caller), value - penalty); + } + + function test_ShouldIncreaseReportersLockedGoldBalance_WhenAccountIsSlashedForAllOfItsLockedGold() + public + { + uint256 penalty = value; + uint256 reward = value / 2; + helper_WhenAccountIsSlashedForAllOfItsLockedGold(penalty, reward); + + assertEq(lockedGold.getAccountNonvotingLockedGold(reporter), reward); + assertEq(lockedGold.getAccountTotalLockedGold(reporter), reward); + } + + function test_ShouldIncreaseCommunityFundLockedGoldBalance_WhenAccountIsSlashedForAllOfItsLockedGold() + public + { + uint256 penalty = value; + uint256 reward = value / 2; + helper_WhenAccountIsSlashedForAllOfItsLockedGold(penalty, reward); + + assertEq(address(governance).balance, penalty - reward); + } + + // original tests regarding delegating are already covered in other tests below + + function test_ShouldRevert_WhenTheSlashingContractIsRemovedFromIsSlasher() public { + uint256 penalty = value; + uint256 reward = value / 2; + lockedGold.removeSlasher(slasherName, 0); + vm.expectRevert("Caller is not a whitelisted slasher."); + helper_WhenAccountIsSlashedForAllOfItsLockedGold(penalty, reward); + } + + function test_ShouldReduceAccountsNonVotingLockedGoldBalance_WhenAccountIsSlashedForOnlyItsNonvotingBalance_WhenTheAccountHasHalfVotingAndHalfNonVotingGold() + public + { + uint256 voting = value / 2; + uint256 nonVoting = value - voting; + uint256 penalty = nonVoting; + uint256 reward = penalty / 2; + electionSlashTest.vote(group, voting, address(0), address(0)); + + helper_WhenAccountIsSlashedForAllOfItsLockedGold(penalty, reward); + assertEq(lockedGold.getAccountNonvotingLockedGold(caller), nonVoting - penalty); + } + + function test_ShouldLeaveTheVotingLockedGold_WhenAccountIsSlashedForOnlyItsNonvotingBalance_WhenTheAccountHasHalfVotingAndHalfNonVotingGold() + public + { + uint256 voting = value / 2; + uint256 nonVoting = value - voting; + uint256 penalty = nonVoting; + uint256 reward = penalty / 2; + electionSlashTest.vote(group, voting, address(0), address(0)); + helper_WhenAccountIsSlashedForAllOfItsLockedGold(penalty, reward); + + assertEq(lockedGold.getAccountTotalLockedGold(caller), value - penalty); + assertEq(electionSlashTest.getTotalVotesByAccount(caller), voting); + } + + function test_ShouldIncreaseTheReportedLockedGold_WhenAccountIsSlashedForOnlyItsNonvotingBalance_WhenTheAccountHasHalfVotingAndHalfNonVotingGold() + public + { + uint256 voting = value / 2; + uint256 nonVoting = value - voting; + uint256 penalty = nonVoting; + uint256 reward = penalty / 2; + electionSlashTest.vote(group, voting, address(0), address(0)); + helper_WhenAccountIsSlashedForAllOfItsLockedGold(penalty, reward); + + assertEq(lockedGold.getAccountNonvotingLockedGold(reporter), reward); + assertEq(lockedGold.getAccountTotalLockedGold(reporter), reward); + } + + function test_ShouldIncreaseTheCommunityFundLockedGold_WhenAccountIsSlashedForOnlyItsNonvotingBalance_WhenTheAccountHasHalfVotingAndHalfNonVotingGold() + public + { + uint256 voting = value / 2; + uint256 nonVoting = value - voting; + uint256 penalty = nonVoting; + uint256 reward = penalty / 2; + electionSlashTest.vote(group, voting, address(0), address(0)); + helper_WhenAccountIsSlashedForAllOfItsLockedGold(penalty, reward); + + assertEq(address(governance).balance, penalty - reward); + } + + function test_ShouldReduceAccountsNonVotingLockedGoldBalance_WhenAccountIsSlashedForItsWholeBalance_WhenTheAccountHasHalfVotingAndHalfNonVotingGold() + public + { + uint256 voting = value / 2; + uint256 penalty = value; + uint256 reward = penalty / 2; + electionSlashTest.vote(group, voting, address(0), address(0)); + + helper_WhenAccountIsSlashedForAllOfItsLockedGold(penalty, reward); + assertEq(lockedGold.getAccountNonvotingLockedGold(caller), 0); + } + + function test_ShouldLeaveTheVotingLockedGold_WhenAccountIsSlashedFoItsWholeBalance_WhenTheAccountHasHalfVotingAndHalfNonVotingGold() + public + { + uint256 voting = value / 2; + uint256 penalty = value; + uint256 reward = penalty / 2; + electionSlashTest.vote(group, voting, address(0), address(0)); + helper_WhenAccountIsSlashedForAllOfItsLockedGold(penalty, reward); + + assertEq(lockedGold.getAccountTotalLockedGold(caller), 0); + assertEq(electionSlashTest.getTotalVotesByAccount(caller), 0); + } + + function test_ShouldIncreaseTheReportedLockedGold_WhenAccountIsSlashedFoItsWholeBalance_WhenTheAccountHasHalfVotingAndHalfNonVotingGold() + public + { + uint256 voting = value / 2; + uint256 penalty = value; + uint256 reward = penalty / 2; + electionSlashTest.vote(group, voting, address(0), address(0)); + helper_WhenAccountIsSlashedForAllOfItsLockedGold(penalty, reward); + + assertEq(lockedGold.getAccountNonvotingLockedGold(reporter), reward); + assertEq(lockedGold.getAccountTotalLockedGold(reporter), reward); + } + + function test_ShouldIncreaseTheCommunityFundLockedGold_WhenAccountIsSlashedFoItsWholeBalance_WhenTheAccountHasHalfVotingAndHalfNonVotingGold() + public + { + uint256 voting = value / 2; + uint256 penalty = value; + uint256 reward = penalty / 2; + electionSlashTest.vote(group, voting, address(0), address(0)); + helper_WhenAccountIsSlashedForAllOfItsLockedGold(penalty, reward); + + assertEq(address(governance).balance, penalty - reward); + } + + function test_ShouldSlashWholeAccountBalance_WhenAccountIsSlashedForMoreThanItsOwnBalance_WhenTheAccountHasHalfVotingAndHalfNonVotingGold() + public + { + uint256 voting = value / 2; + uint256 penalty = value * 2; + uint256 reward = penalty / 2; + electionSlashTest.vote(group, voting, address(0), address(0)); + helper_WhenAccountIsSlashedForAllOfItsLockedGold(penalty, reward); + + assertEq(lockedGold.getAccountNonvotingLockedGold(caller), 0); + assertEq(lockedGold.getAccountTotalLockedGold(caller), 0); + assertEq(electionSlashTest.getTotalVotesByAccount(caller), 0); + } + + function test_ShouldIncreaseTheReportedLockedGold_WhenAccountIsSlashedForMoreThanItsOwnBalance_WhenTheAccountHasHalfVotingAndHalfNonVotingGold() + public + { + uint256 voting = value / 2; + uint256 penalty = value * 2; + uint256 reward = penalty / 2; + electionSlashTest.vote(group, voting, address(0), address(0)); + helper_WhenAccountIsSlashedForAllOfItsLockedGold(penalty, reward); + + assertEq(lockedGold.getAccountNonvotingLockedGold(reporter), reward); + assertEq(lockedGold.getAccountTotalLockedGold(reporter), reward); + } + + function test_ShouldIncreaseTheCommunityFundLockedGoldOnlySlashedAccountTotalBalanceMinusReward_WhenAccountIsSlashedForMoreThanItsOwnBalance_WhenTheAccountHasHalfVotingAndHalfNonVotingGold() + public + { + uint256 voting = value / 2; + uint256 penalty = value * 2; + uint256 reward = penalty / 2; + electionSlashTest.vote(group, voting, address(0), address(0)); + helper_WhenAccountIsSlashedForAllOfItsLockedGold(penalty, reward); + + assertEq(address(governance).balance, value - reward); + } + + function test_ShouldRevertWhenCalledByNonAccountReporters() public { + address[] memory lessers = new address[](1); + lessers[0] = address(0); + address[] memory greaters = new address[](1); + greaters[0] = address(0); + + uint256[] memory indices = new uint256[](1); + indices[0] = 0; + + vm.prank(randomAddress); + vm.expectRevert("Caller is not a whitelisted slasher."); + lockedGold.slash(caller, value, reporter, value / 2, lessers, greaters, indices); + } + + function test_ShouldAllowToSlashByAccountSigner() public { + address signerReporter = actor("signerReporter"); + bytes32 role = hex"0000000000000000000000000000000000000000000000000000000000001337"; + vm.prank(reporter); + accounts.authorizeSigner(signerReporter, role); + vm.prank(signerReporter); + accounts.completeSignerAuthorization(reporter, role); + + uint256 reward = value / 2; + + helper_WhenAccountIsSlashedForAllOfItsLockedGold(value, reward); + assertEq(lockedGold.getAccountNonvotingLockedGold(reporter), reward); + assertEq(lockedGold.getAccountTotalLockedGold(reporter), reward); + } +} + +contract LockedGoldDelegateGovernanceVotes is LockedGoldTest { + address delegatee1 = actor("delegatee1"); + address delegatee2 = actor("delegatee2"); + address delegatee3 = actor("delegatee3"); + address delegator = actor("delegator"); + address delegator2 = actor("delegator2"); + + address delegatorSigner; + uint256 delegatorSignerPK; + address delegatorSigner2; + uint256 delegatorSigner2PK; + address delegateeSigner1; + uint256 delegateeSigner1PK; + address delegateeSigner2; + uint256 delegateeSigner2PK; + + uint256 value = 1000; + uint256 percentToDelegate = 30; + uint256 delegatedAmount = (value * percentToDelegate) / 100; + + uint256 percentToDelegate1 = 30; + uint256 percentToDelegate2 = 20; + uint256 percentToDelegate3 = 50; + uint256 delegatedAmount1 = (value * percentToDelegate1) / 100; + uint256 delegatedAmount2 = (value * percentToDelegate2) / 100; + uint256 delegatedAmount3 = (value * percentToDelegate3) / 100; + + function setUp() public { + super.setUp(); + + vm.prank(delegatee1); + accounts.createAccount(); + vm.prank(delegatee2); + accounts.createAccount(); + vm.prank(delegatee3); + accounts.createAccount(); + vm.prank(delegator); + accounts.createAccount(); + vm.prank(delegator2); + accounts.createAccount(); + + (delegatorSigner, delegatorSignerPK) = actorWithPK("delegatorSigner"); + (delegatorSigner2, delegatorSigner2PK) = actorWithPK("delegatorSigner2"); + (delegateeSigner1, delegateeSigner1PK) = actorWithPK("delegateeSigner1"); + (delegateeSigner2, delegateeSigner2PK) = actorWithPK("delegateeSigner2"); + + vm.deal(delegator, 10 ether); + vm.deal(delegator2, 10 ether); + } + + function whenVoteSigner_LockedGoldDelegateGovernanceVotes() public { + helper_WhenVoteSigners( + WhenVoteSignerStruct( + delegator, + delegator2, + delegatee1, + delegatee2, + delegatorSignerPK, + delegateeSigner1PK, + delegatorSigner2PK, + delegateeSigner2PK, + true + ) + ); + } + + function test_ShouldRevertWhenDelegateeIsNotAccount() public { + vm.expectRevert("Must first register address with Account.createAccount"); + lockedGold.delegateGovernanceVotes(randomAddress, FixidityLib.newFixedFraction(1, 1).unwrap()); + } + + function test_ShouldRevert_WhenDelegatorIsNotAnAccount() public { + vm.expectRevert("Must first register address with Account.createAccount"); + vm.prank(randomAddress); + lockedGold.delegateGovernanceVotes(caller, FixidityLib.newFixedFraction(1, 1).unwrap()); + } + + function test_ShouldReturnCorrectDelegatedAmount_WhenNoGoldIsLocked_WhenNoVoteSigners() public { + delegateCelo(delegator, delegatee1, percentToDelegate); + + assertDelegatorDelegateeAmounts(delegator, delegatee1, percentToDelegate, 0); + assertEq( + FixidityLib.wrap(lockedGold.getAccountTotalDelegatedFraction(delegator) * 100).fromFixed(), + percentToDelegate + ); + } + + function test_ShouldEmitCeloDelegatedEvent_WhenNoGoldIsLocked_WhenNoVoteSigner() public { + vm.expectEmit(true, true, true, true); + emit CeloDelegated( + delegator, + delegatee1, + FixidityLib.newFixedFraction(percentToDelegate, 100).unwrap(), + 0 + ); + delegateCelo(delegator, delegatee1, percentToDelegate); + } + + function test_ShouldRevert_WhenDelegatingAsValidator() public { + validators.setValidator(delegator); + vm.expectRevert("Validators cannot delegate votes."); + delegateCelo(delegator, delegatee1, 100); + } + + function test_ShouldRevert_WhenDelegatingAsValidatorGroup() public { + validators.setValidatorGroup(delegator); + vm.expectRevert("Validator groups cannot delegate votes."); + delegateCelo(delegator, delegatee1, 100); + } + + function test_ShouldRevertWhenIncorrectPercentAmountIsInserted() public { + vm.expectRevert("Delegate fraction must be less than or equal to 1"); + delegateCelo(delegator, delegatee1, 101); + } + + function test_ShouldRevertWhenDelegatingVotesThatAreCurrentlyVotingForProposal_WhenDelegatorIsVotingInReferendum_WhenSomeGoldIsLocked() + public + { + lockCelo(delegator, value); + governance.setTotalVotes(delegator, 1); + + vm.expectRevert("Cannot delegate votes that are voting in referendum"); + delegateCelo(delegator, delegatee1, 100); + } + + function test_ShouldRevertWhenDelegatingVotesThatAreCurrentlyVotingForProposal2Delegatees_WhenDelegatorIsVotingInReferendum_WhenSomeGoldIsLocked() + public + { + lockCelo(delegator, value); + governance.setTotalVotes(delegator, 1); + + delegateCelo(delegator, delegatee1, 99); + + vm.expectRevert("Cannot delegate votes that are voting in referendum"); + delegateCelo(delegator, delegatee2, 1); + } + + function test_ShouldDelegateWhenVotingForLessThanRequestedForDelegatetion_WhenDelegatorIsVotingInReferendum_WhenSomeGoldIsLocked() + public + { + lockCelo(delegator, value); + governance.setTotalVotes(delegator, 1); + + delegateCelo(delegator, delegatee1, 99); + + assertEq(lockedGold.getAccountTotalGovernanceVotingPower(delegator), 10); + assertEq(lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), 990); + } + + function test_ShouldRevertWhenDelegatingMoreThan100PercentInTwoStepsForDifferentDelegatees_WhenDelegatingToDelegatee1_WhenSomeGoldIsLocked() + public + { + lockCelo(delegator, value); + delegateCelo(delegator, delegatee1, 10); + vm.expectRevert("Cannot delegate more than 100%"); + delegateCelo(delegator, delegatee2, 100); + } + + function test_ShouldDelegateCorrectlyWhenDelegatedToSameDelegateeInTwoSteps_WhenSomeGoldIsLocked() + public + { + lockCelo(delegator, value); + delegateCelo(delegator, delegatee1, 10); + delegateCelo(delegator, delegatee1, 100); + + assertEq(lockedGold.getAccountTotalGovernanceVotingPower(delegator), 0); + assertEq(lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), 1000); + } + + function test_ShouldEmitCeloDelegatedEvent_WhenSomeGoldIsLocked() public { + lockCelo(delegator, value); + vm.expectEmit(true, true, true, true); + emit CeloDelegated( + delegator, + delegatee1, + FixidityLib.newFixedFraction(percentToDelegate, 100).unwrap(), + delegatedAmount + ); + + delegateCelo(delegator, delegatee1, percentToDelegate); + } + + function test_ShouldDelegateVotesCorrectly_WhenSomeGoldIsLocked() public { + lockCelo(delegator, value); + delegateCelo(delegator, delegatee1, percentToDelegate); + + assertDelegatorDelegateeAmounts(delegator, delegatee1, percentToDelegate, delegatedAmount); + } + + function test_ShouldDelegateVotesCorrectlyToMultipleAccounts_WhenSomeGoldIsLocked() public { + lockCelo(delegator, value); + delegateCelo(delegator, delegatee1, percentToDelegate1); + delegateCelo(delegator, delegatee2, percentToDelegate2); + delegateCelo(delegator, delegatee3, percentToDelegate3); + + assertDelegatorDelegateeAmounts(delegator, delegatee1, percentToDelegate1, delegatedAmount1); + assertDelegatorDelegateeAmounts(delegator, delegatee2, percentToDelegate2, delegatedAmount2); + assertDelegatorDelegateeAmounts(delegator, delegatee3, percentToDelegate3, delegatedAmount3); + } + + function test_ShouldDelegateVotesCorrectly_WhenLockedMoreGoldAndRedelegate_WhenSomeGoldIsLocked() + public + { + lockCelo(delegator, value); + delegateCelo(delegator, delegatee1, percentToDelegate); + + lockCelo(delegator, value); + delegateCelo(delegator, delegatee1, percentToDelegate); + + assertDelegatorDelegateeAmounts(delegator, delegatee1, percentToDelegate, delegatedAmount * 2); + } + + function test_ShouldEmitTheCeloDelegatedEvent_WhenLockedMoreGoldAndRedelegate_WhenSomeGoldIsLocked() + public + { + lockCelo(delegator, value); + delegateCelo(delegator, delegatee1, percentToDelegate); + lockCelo(delegator, value); + + vm.expectEmit(true, true, true, true); + emit CeloDelegated( + delegator, + delegatee1, + FixidityLib.newFixedFraction(percentToDelegate, 100).unwrap(), + delegatedAmount * 2 + ); + + delegateCelo(delegator, delegatee1, percentToDelegate); + } + + function test_ShouldDelegateVotesCorrectly_When2DelegatorsAreDelegatingToDelegatee() public { + lockCelo(delegator, value); + delegateCelo(delegator, delegatee1, percentToDelegate1); + + lockCelo(delegator2, value); + delegateCelo(delegator2, delegatee1, percentToDelegate2); + + assertDelegatorDelegateeAmounts(delegator, delegatee1, percentToDelegate1, delegatedAmount1); + assertDelegatorDelegateeAmounts(delegator2, delegatee1, percentToDelegate2, delegatedAmount2); + } + + function test_ShouldRevertWhenIncorrectPercentAmountIsInserted_WhenSomeGoldIsLocked_WhenVoteSigners() + public + { + whenVoteSigner_LockedGoldDelegateGovernanceVotes(); + vm.expectRevert("Delegate fraction must be less than or equal to 1"); + delegateCelo(delegatorSigner, delegatee1, 101); + } + + function test_ShouldRevertWhenDelegatingVotesThatAreCurrentlyVotingForProposal_WhenVoteSigners() + public + { + whenVoteSigner_LockedGoldDelegateGovernanceVotes(); + governance.setTotalVotes(delegator, 1); + vm.expectRevert("Cannot delegate votes that are voting in referendum"); + delegateCelo(delegatorSigner, delegatee1, 100); + } + + function test_ShouldRevertWhenVotingForProposalWIthVotesThatAreCurrentlyUsedInReferendum2Delegatees_WhenVoteSigners() + public + { + whenVoteSigner_LockedGoldDelegateGovernanceVotes(); + governance.setTotalVotes(delegator, 1); + delegateCelo(delegatorSigner, delegatee1, 99); + vm.expectRevert("Cannot delegate votes that are voting in referendum"); + delegateCelo(delegatorSigner, delegatee2, 1); + } + + function test_ShouldDelegate_WhenVotingForLessThanRequestedForDelegation_WHenVoteSigners() + public + { + whenVoteSigner_LockedGoldDelegateGovernanceVotes(); + governance.setTotalVotes(delegator, 1); + delegateCelo(delegatorSigner, delegatee1, 99); + + assertEq(lockedGold.getAccountTotalGovernanceVotingPower(delegator), 10); + assertEq(lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), 990); + } + + function test_ShouldRevertWhenDelegatingMoreThan100PercentInTwoStepsToTwoDifferentDelegatees_WhenVoteSigners() + public + { + whenVoteSigner_LockedGoldDelegateGovernanceVotes(); + delegateCelo(delegatorSigner, delegatee1, 10); + vm.expectRevert("Cannot delegate more than 100%"); + delegateCelo(delegatorSigner, delegatee2, 100); + } + + function test_ShouldDelegateCorrectlyWhenDelegatedToSameAccountInTwoSteps_WhenVoteSigners() + public + { + whenVoteSigner_LockedGoldDelegateGovernanceVotes(); + + delegateCelo(delegatorSigner, delegatee1, 10); + delegateCelo(delegatorSigner, delegatee1, 100); + + assertEq(lockedGold.getAccountTotalGovernanceVotingPower(delegator), 0); + assertEq(lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), 1000); + } + + function test_ShouldEmitCeloDelegatedEvent_WhenVoteSigners() public { + whenVoteSigner_LockedGoldDelegateGovernanceVotes(); + + vm.expectEmit(true, true, true, true); + emit CeloDelegated( + delegator, + delegatee1, + FixidityLib.newFixedFraction(percentToDelegate, 100).unwrap(), + delegatedAmount + ); + + delegateCelo(delegatorSigner, delegatee1, percentToDelegate); + } + + function test_ShouldDelegateVotesCorrectly_WhenVoteSigners() public { + whenVoteSigner_LockedGoldDelegateGovernanceVotes(); + + delegateCelo(delegatorSigner, delegatee1, percentToDelegate); + assertDelegatorDelegateeAmounts(delegator, delegatee1, percentToDelegate, delegatedAmount); + } + + function test_ShouldDelegateVotesCorrectlyToMultipleAccounts_WhenVoteSigners() public { + whenVoteSigner_LockedGoldDelegateGovernanceVotes(); + uint256 percentToDelegate1 = 30; + uint256 percentToDelegate2 = 20; + uint256 delegatedAmount1 = 300; + uint256 delegatedAmount2 = 200; + + delegateCelo(delegatorSigner, delegatee1, percentToDelegate1); + delegateCelo(delegatorSigner2, delegatee2, percentToDelegate2); + + assertDelegatorDelegateeAmounts(delegator, delegatee1, percentToDelegate1, delegatedAmount1); + assertDelegatorDelegateeAmounts(delegator2, delegatee2, percentToDelegate2, delegatedAmount2); + } + + function test_ShouldDelegateVotesCorrectly_WhenLockedMoreGoldAndRedelegate_WhenVoteSigners() + public + { + whenVoteSigner_LockedGoldDelegateGovernanceVotes(); + uint256 percentToDelegate1 = 30; + uint256 delegatedAmount1 = 300; + + delegateCelo(delegatorSigner, delegateeSigner1, percentToDelegate1); + + lockCelo(delegator, value); + delegateCelo(delegatorSigner, delegatee1, percentToDelegate1); + + assertDelegatorDelegateeAmounts( + delegator, + delegatee1, + percentToDelegate1, + delegatedAmount1 * 2 + ); + } + + function test_ShouldEmitCeloDelegated_WhenLockedMoreGoldAndRedelegate_WhenVoteSigners() public { + whenVoteSigner_LockedGoldDelegateGovernanceVotes(); + uint256 percentToDelegate1 = 30; + uint256 delegatedAmount1 = 300; + + delegateCelo(delegatorSigner, delegatee1, percentToDelegate1); + + lockCelo(delegator, value); + vm.expectEmit(true, true, true, true); + emit CeloDelegated( + delegator, + delegatee1, + FixidityLib.newFixedFraction(percentToDelegate1, 100).unwrap(), + delegatedAmount1 * 2 + ); + delegateCelo(delegatorSigner, delegatee1, percentToDelegate1); + } + + function shouldRevertWhenTryingToAddExtraDelegatee(address _delegatorSinger) public { + lockedGold.setMaxDelegateesCount(2); + lockCelo(delegator, value); + delegateCelo(_delegatorSinger, delegatee1, 50); + delegateCelo(_delegatorSinger, delegatee2, 50); + + address[] memory delegateesOfDelegator = lockedGold.getDelegateesOfDelegator(delegator); + assertEq(delegateesOfDelegator.length, 2); + assertEq(delegateesOfDelegator[0], delegatee1); + assertEq(delegateesOfDelegator[1], delegatee2); + + vm.expectRevert("Too many delegatees"); + delegateCelo(_delegatorSinger, delegatee3, 50); + } + + function test_ShouldRevertWhenTryingToAddExtraDelegatee() public { + shouldRevertWhenTryingToAddExtraDelegatee(delegator); + } + + function test_ShouldRevertWhenTryingToAddExtraDelegatee_WhenVoteSigners() public { + whenVoteSigner_LockedGoldDelegateGovernanceVotes(); + shouldRevertWhenTryingToAddExtraDelegatee(delegatorSigner); + } + + function shouldAllowToAddExtraDelegatee_WhenLimitIsIncreased(address _delegatorSigner) public { + lockedGold.setMaxDelegateesCount(2); + lockCelo(delegator, value); + delegateCelo(_delegatorSigner, delegatee1, 50); + delegateCelo(_delegatorSigner, delegatee2, 40); + + address[] memory delegateesOfDelegator = lockedGold.getDelegateesOfDelegator(delegator); + assertEq(delegateesOfDelegator.length, 2); + assertEq(delegateesOfDelegator[0], delegatee1); + assertEq(delegateesOfDelegator[1], delegatee2); + lockedGold.setMaxDelegateesCount(3); + vm.prank(_delegatorSigner); + lockedGold.delegateGovernanceVotes(delegatee3, FixidityLib.newFixedFraction(10, 100).unwrap()); + } + + function test_ShouldAllowToAddExtraDelegatee_WhenLimitIsIncreased() public { + shouldAllowToAddExtraDelegatee_WhenLimitIsIncreased(delegator); + } + + function test_ShouldAllowToAddExtraDelegatee_WhenLimitIsIncreased_WhenVoteSigners() public { + whenVoteSigner_LockedGoldDelegateGovernanceVotes(); + shouldAllowToAddExtraDelegatee_WhenLimitIsIncreased(delegatorSigner); + } +} + +contract LockedGoldRevokeDelegatedGovernanceVotes is LockedGoldTest { + address delegatee1 = actor("delegatee1"); + address delegatee2 = actor("delegatee2"); + address delegatee3 = actor("delegatee3"); + address delegator = actor("delegator"); + address delegator2 = actor("delegator2"); + + address delegatorSigner; + uint256 delegatorSignerPK; + address delegatorSigner2; + uint256 delegatorSigner2PK; + address delegateeSigner1; + uint256 delegateeSigner1PK; + address delegateeSigner2; + uint256 delegateeSigner2PK; + + uint256 value = 1000; + uint256 percentageToRevoke = 2; + uint256 percentageToDelegate = 10; + uint256 delegatedAmount = (value * percentageToDelegate) / 100; + uint256 amountToRevoke = (value / 100) * percentageToRevoke; + uint256 votingWeight = 100; + uint256 votingAmount = (delegatedAmount * 2 - amountToRevoke); + + function setUp() public { + super.setUp(); + + vm.prank(delegatee1); + accounts.createAccount(); + vm.prank(delegatee2); + accounts.createAccount(); + vm.prank(delegatee3); + accounts.createAccount(); + vm.prank(delegator); + accounts.createAccount(); + vm.prank(delegator2); + accounts.createAccount(); + + (delegatorSigner, delegatorSignerPK) = actorWithPK("delegatorSigner"); + (delegatorSigner2, delegatorSigner2PK) = actorWithPK("delegatorSigner2"); + (delegateeSigner1, delegateeSigner1PK) = actorWithPK("delegateeSigner1"); + (delegateeSigner2, delegateeSigner2PK) = actorWithPK("delegateeSigner2"); + + vm.deal(delegator, 10 ether); + vm.deal(delegator2, 10 ether); + } + + function whenVoteSigner_LockedGoldRevokeDelegatedGovernanceVotes() public { + helper_WhenVoteSigners( + WhenVoteSignerStruct( + delegator, + delegator2, + delegatee1, + delegatee2, + delegatorSignerPK, + delegateeSigner1PK, + delegatorSigner2PK, + delegateeSigner2PK, + true + ) + ); + } + + function shouldRevokeVotesCorrectlyWhenDelegateeVoting_WhenRevokingPercentageSuchAsThatWithNewlyLockedAmountIwWouldDecreaseBelowZero( + address _delegatorSigner, + address _delegatorSigner2, + bool lock + ) public { + if (lock) { + lockCelo(delegator, value); + lockCelo(delegator2, value); + } + uint256 percentageToRevokeAfterLock = 6; + uint256 amountFromDelegator1AfterRevoke = ((2 * delegatedAmount) / percentageToDelegate) * + (percentageToDelegate - percentageToRevoke - percentageToRevokeAfterLock); + + delegateCelo(_delegatorSigner, delegatee1, percentageToDelegate); + delegateCelo(_delegatorSigner2, delegatee1, percentageToDelegate); + revokeDelegatedVotes(_delegatorSigner, delegatee1, percentageToRevoke); + assertEq( + lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), + delegatedAmount * 2 - amountToRevoke + ); + + lockCelo(delegator, value); + governance.setTotalVotes(delegatee1, votingAmount); + revokeDelegatedVotes(_delegatorSigner, delegatee1, percentageToRevokeAfterLock); + + assertDelegatedVotes_ShouldRevokeVotesCorrectlyWhenDelegateeVoting( + amountFromDelegator1AfterRevoke, + percentageToRevokeAfterLock + ); + } + + function shouldRevokeVotesCorrectlyWhenDelegateeVoting_WhenRevokingPercentageSuchAsThatWithNewlyLockedAmountIwWouldNotDecreaseBelowZero( + address _delegatorSigner, + address _delegatorSigner2, + bool lock + ) public { + if (lock) { + lockCelo(delegator, value); + lockCelo(delegator2, value); + } + uint256 percentageToRevokeAfterLock = 2; + uint256 amountFromDelegator1AfterRevoke = ((2 * delegatedAmount) / percentageToDelegate) * + (percentageToDelegate - percentageToRevoke - percentageToRevokeAfterLock); + + delegateCelo(_delegatorSigner, delegatee1, percentageToDelegate); + delegateCelo(_delegatorSigner2, delegatee1, percentageToDelegate); + revokeDelegatedVotes(_delegatorSigner, delegatee1, percentageToRevoke); + + assertEq( + lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), + delegatedAmount * 2 - amountToRevoke + ); + + lockCelo(delegator, value); + governance.setTotalVotes(delegatee1, votingAmount); + revokeDelegatedVotes(_delegatorSigner, delegatee1, percentageToRevokeAfterLock); + + assertDelegatedVotes_ShouldRevokeVotesCorrectlyWhenDelegateeVoting( + amountFromDelegator1AfterRevoke, + percentageToRevokeAfterLock + ); + assertEq(governance.removeVotesCalledFor(delegatee1), 0); + } + + function assertDelegatedVotes_ShouldRevokeVotesCorrectlyWhenDelegateeVoting( + uint256 amountFromDelegator1AfterRevoke, + uint256 percentageToRevokeAfterLock + ) public { + assertEq( + lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), + delegatedAmount + amountFromDelegator1AfterRevoke + ); + assertDelegatorDelegateeAmounts( + delegator, + delegatee1, + percentageToDelegate - percentageToRevoke - percentageToRevokeAfterLock, + amountFromDelegator1AfterRevoke + ); + assertDelegatorDelegateeAmounts(delegator2, delegatee1, percentageToDelegate, delegatedAmount); + assertEq( + lockedGold.totalDelegatedCelo(delegatee1), + delegatedAmount + amountFromDelegator1AfterRevoke + ); + } + + function shouldRevokeCorrectlyWhenDelegateeVoting( + address _delegator, + address _delegatorSigner, + address _delegatee1, + address _delegatee2, + bool lock + ) public { + if (lock) { + lockCelo(_delegator, value); + } + delegateCelo(_delegatorSigner, _delegatee1, percentageToDelegate); + delegateCelo(_delegatorSigner, _delegatee2, percentageToDelegate); + governance.setTotalVotes(_delegatee1, votingWeight); + revokeDelegatedVotes(_delegatorSigner, _delegatee1, percentageToRevoke); + + assertDelegatedVotes_ShouldRevokeCorrectly(); + } + + function shouldRevokeCorrectlyWhenDelegateeNotVoting(address _delegatorSigner, bool lockGold) + public + { + if (lockGold) { + lockCelo(delegator, value); + } + delegateCelo(_delegatorSigner, delegatee1, percentageToDelegate); + delegateCelo(_delegatorSigner, delegatee2, percentageToDelegate); + revokeDelegatedVotes(_delegatorSigner, delegatee1, percentageToRevoke); + + assertDelegatedVotes_ShouldRevokeCorrectly(); + } + + function assertDelegatedVotes_ShouldRevokeCorrectly() public { + assertEq( + lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), + delegatedAmount - amountToRevoke + ); + + assertDelegatorDelegateeAmounts( + delegator, + delegatee1, + percentageToDelegate - percentageToRevoke, + delegatedAmount - amountToRevoke + ); + + assertEq(lockedGold.getAccountTotalGovernanceVotingPower(delegatee2), delegatedAmount); + assertDelegatorDelegateeAmounts(delegator, delegatee2, percentageToDelegate, delegatedAmount); + assertEq(lockedGold.totalDelegatedCelo(delegatee1), delegatedAmount - amountToRevoke); + assertEq(lockedGold.totalDelegatedCelo(delegatee2), delegatedAmount); + } + + function test_ShouldRevertWhenIncorrectPercentAmountIsInserted() public { + vm.expectRevert("Revoke fraction must be less than or equal to 1"); + lockedGold.revokeDelegatedGovernanceVotes( + address(0), + FixidityLib.newFixedFraction(101, 100).unwrap() + ); + } + + function test_ShouldRevertWhenNotingIsDelegated() public { + vm.expectRevert("Not enough total delegated percents"); + lockedGold.revokeDelegatedGovernanceVotes( + address(0), + FixidityLib.newFixedFraction(1, 1).unwrap() + ); + } + + function test_ShouldRevertWhenTryingToRevertMorePercentThanDelegated() public { + lockCelo(delegator, 100); + delegateCelo(delegator, delegatee1, 50); + + vm.expectRevert("Not enough total delegated percents"); + revokeDelegatedVotes(delegator, delegatee1, 51); + } + + function test_ShouldRevokeVotesCorrectly_WhenDelegateeNotVoting() public { + lockCelo(delegator, value); + delegateCelo(delegator, delegatee1, percentageToDelegate); + revokeDelegatedVotes(delegator, delegatee1, percentageToRevoke); + + assertDelegatorDelegateeAmounts( + delegator, + delegatee1, + percentageToDelegate - percentageToRevoke, + delegatedAmount - amountToRevoke + ); + + assertEq( + lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), + delegatedAmount - amountToRevoke + ); + assertEq(lockedGold.totalDelegatedCelo(delegatee1), delegatedAmount - amountToRevoke); + } + + function test_ShouldEmitDelegatedCeloRevokedEvent() public { + lockCelo(delegator, value); + delegateCelo(delegator, delegatee1, percentageToDelegate); + + vm.expectEmit(true, true, true, true); + emit DelegatedCeloRevoked( + delegator, + delegatee1, + FixidityLib.newFixedFraction(percentageToRevoke, 100).unwrap(), + amountToRevoke + ); + revokeDelegatedVotes(delegator, delegatee1, percentageToRevoke); + } + + function test_ShouldRevokeVotesCorrectlyWhenDelegateeVoting_WhenRevokingPercentageSuchAsThatWithNewlyLockedAmountIwWouldDecreaseBelowZero() + public + { + shouldRevokeVotesCorrectlyWhenDelegateeVoting_WhenRevokingPercentageSuchAsThatWithNewlyLockedAmountIwWouldDecreaseBelowZero( + delegator, + delegator2, + true + ); + } + + function test_ShouldRevokeVotesCorrectlyWhenDelegateeVoting_WhenRevokingPercentageSuchAsThatWithNewlyLockedAmountIwWouldNotDecreaseBelowZero() + public + { + shouldRevokeVotesCorrectlyWhenDelegateeVoting_WhenRevokingPercentageSuchAsThatWithNewlyLockedAmountIwWouldNotDecreaseBelowZero( + delegator, + delegator2, + true + ); + } + + function test_ShouldRevokeVotesCorrectlyWhenDelegateeNotVoting_WhenDelegatedTo2Accounts() public { + shouldRevokeCorrectlyWhenDelegateeNotVoting(delegator, true); + } + + function test_ShouldRevokeVotesCorrectlyWhenDelegateeVoting_WhenDelegatedTo2Accounts() public { + shouldRevokeCorrectlyWhenDelegateeVoting(delegator, delegator, delegatee1, delegatee2, true); + } + + function test_ShouldRevertWhenTryingToRevertMorePercentThanDelegated_WhenVoteSigners() public { + whenVoteSigner_LockedGoldRevokeDelegatedGovernanceVotes(); + delegateCelo(delegatorSigner, delegateeSigner1, 50); + + vm.expectRevert("Not enough total delegated percents"); + revokeDelegatedVotes(delegatorSigner, delegateeSigner1, 51); + } + + function test_ShouldRevokeVotesCorrectly_WhenDelegateeNotVoting_WhenVoteSigners() public { + whenVoteSigner_LockedGoldRevokeDelegatedGovernanceVotes(); + + delegateCelo(delegatorSigner, delegateeSigner1, percentageToDelegate); + revokeDelegatedVotes(delegatorSigner, delegateeSigner1, percentageToRevoke); + + assertDelegatorDelegateeAmounts( + delegator, + delegatee1, + percentageToDelegate - percentageToRevoke, + delegatedAmount - amountToRevoke + ); + + assertEq( + lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), + delegatedAmount - amountToRevoke + ); + assertEq(lockedGold.totalDelegatedCelo(delegatee1), delegatedAmount - amountToRevoke); + } + + function test_ShouldEmitDelegatedCeloRevokedEvent_WhenVoteSigners() public { + whenVoteSigner_LockedGoldRevokeDelegatedGovernanceVotes(); + delegateCelo(delegatorSigner, delegatee1, percentageToDelegate); + + vm.expectEmit(true, true, true, true); + emit DelegatedCeloRevoked( + delegator, + delegatee1, + FixidityLib.newFixedFraction(percentageToRevoke, 100).unwrap(), + amountToRevoke + ); + revokeDelegatedVotes(delegatorSigner, delegatee1, percentageToRevoke); + } + + function test_ShouldRevokeVotesCorrectlyWhenDelegateeVoting_WhenRevokingPercentageSuchAsThatWithNewlyLockedAmountIwWouldDecreaseBelowZero_WhenVoteSigners() + public + { + whenVoteSigner_LockedGoldRevokeDelegatedGovernanceVotes(); + shouldRevokeVotesCorrectlyWhenDelegateeVoting_WhenRevokingPercentageSuchAsThatWithNewlyLockedAmountIwWouldDecreaseBelowZero( + delegatorSigner, + delegatorSigner2, + false + ); + } + + function test_ShouldRevokeVotesCorrectlyWhenDelegateeVoting_WhenRevokingPercentageSuchAsThatWithNewlyLockedAmountIwWouldNotDecreaseBelowZero_WhenVoteSigners() + public + { + whenVoteSigner_LockedGoldRevokeDelegatedGovernanceVotes(); + shouldRevokeVotesCorrectlyWhenDelegateeVoting_WhenRevokingPercentageSuchAsThatWithNewlyLockedAmountIwWouldNotDecreaseBelowZero( + delegatorSigner, + delegatorSigner2, + false + ); + } + + function test_ShouldRevokeVotesCorrectlyWhenDelegateeNotVoting_WhenDelegatedTo2Accounts_WhenVoteSigners() + public + { + whenVoteSigner_LockedGoldRevokeDelegatedGovernanceVotes(); + shouldRevokeCorrectlyWhenDelegateeNotVoting(delegatorSigner, false); + } + + function test_ShouldRevokeVotesCorrectlyWhenDelegateeVoting_WhenDelegatedTo2Accounts_WhenVoteSigners() + public + { + whenVoteSigner_LockedGoldRevokeDelegatedGovernanceVotes(); + shouldRevokeCorrectlyWhenDelegateeVoting( + delegator, + delegatorSigner, + delegatee1, + delegatee2, + false + ); + } +} + +contract LockedGoldGetAccountTotalGovernanceVotingPower is LockedGoldTest { + address delegator = actor("delegator"); + address delegatee = actor("delegatee"); + uint256 value = 1000; + + uint256 delegatedPercent = 70; + uint256 delegatedAmount = (value / 100) * delegatedPercent; + + function setUp() public { + super.setUp(); + + vm.deal(delegator, 10 ether); + vm.deal(delegatee, 10 ether); + + vm.prank(delegator); + accounts.createAccount(); + vm.prank(delegatee); + accounts.createAccount(); + + vm.prank(delegator); + lockedGold.lock.value(value)(); + } + + function test_ShouldReturn0WhenNothingLockedNorAccount() public { + assertEq(lockedGold.getAccountTotalGovernanceVotingPower(delegatee), 0); + } + + function test_ShouldReturnCorrectValueWhenLockedAndDelegatedForDelegateeAndDelegator_WhenOnlyDelegated_WhenHavingAccounts() + public + { + delegateCelo(delegator, delegatee, delegatedPercent); + + assertEq(lockedGold.getAccountTotalGovernanceVotingPower(delegatee), delegatedAmount); + assertEq(lockedGold.getAccountTotalGovernanceVotingPower(delegator), value - delegatedAmount); + } + + function test_ShouldReturnCorrectValueWhenLockedAndDelegatedForDelegateeAndDelegator_WhenDelegateeHasLockedCelo_WhenHavingAccounts() + public + { + lockCelo(delegatee, value); + delegateCelo(delegator, delegatee, delegatedPercent); + + assertEq(lockedGold.getAccountTotalGovernanceVotingPower(delegatee), delegatedAmount + value); + assertEq(lockedGold.getAccountTotalGovernanceVotingPower(delegator), value - delegatedAmount); + } +} + +contract LockedGoldGetDelegatorDelegateeInfo is LockedGoldTest { + address delegator = actor("delegator"); + address delegatee = actor("delegatee"); + uint256 value = 1000; + uint256 percent = 70; + uint256 amount = (value / 100) * percent; + + function setUp() public { + super.setUp(); + + vm.prank(delegator); + accounts.createAccount(); + vm.prank(delegatee); + accounts.createAccount(); + + vm.deal(delegator, 10 ether); + vm.deal(delegatee, 10 ether); + } + + function test_ShouldReturn0WhenNothingDelegated() public { + (uint256 fraction, uint256 currentAmount) = lockedGold.getDelegatorDelegateeInfo( + delegator, + delegatee + ); + assertEq(FixidityLib.wrap(fraction * 100).fromFixed(), 0); + assertEq(currentAmount, 0); + } + + function test_ShouldReturnCorrectPercentAndAmount_WhenLockedCelo() public { + lockCelo(delegator, value); + lockCelo(delegatee, value); + delegateCelo(delegator, delegatee, percent); + + (uint256 fraction, uint256 currentAmount) = lockedGold.getDelegatorDelegateeInfo( + delegator, + delegatee + ); + assertEq(FixidityLib.wrap(fraction * 100).fromFixed(), percent); + assertEq(currentAmount, amount); + } +} + +contract LockedGoldGetDelegatorDelegateeExpectedAndRealAmount is LockedGoldTest { + address delegator = actor("delegator"); + address delegatee = actor("delegatee"); + address delegatorSigner; + uint256 delegatorSignerPK; + address delegateeSigner; + uint256 delegateeSignerPK; + uint256 value = 1000; + uint256 percent = 70; + uint256 amount = (value / 100) * percent; + + function setUp() public { + super.setUp(); + + vm.prank(delegator); + accounts.createAccount(); + vm.prank(delegatee); + accounts.createAccount(); + + vm.deal(delegator, 10 ether); + vm.deal(delegatee, 10 ether); + + vm.prank(delegator); + lockedGold.lock.value(value)(); + vm.prank(delegatee); + lockedGold.lock.value(value)(); + + (delegatorSigner, delegatorSignerPK) = actorWithPK("delegatorSigner"); + (delegateeSigner, delegateeSignerPK) = actorWithPK("delegateeSigner"); + } + + function whenVoteSigner_LockedGoldGetDelegatorDelegateeExpectedAndRealAmount() public { + helper_WhenVoteSigners( + WhenVoteSignerStruct( + delegator, + address(0), + delegatee, + address(0), + delegatorSignerPK, + delegateeSignerPK, + 0, + 0, + false + ) + ); + } + + function assertDelegatorDelegateeExpectedAndRealAmount( + address _delegator, + address _delegatee, + uint256 expected, + uint256 real + ) public { + (uint256 expectedAmount, uint256 realAmount) = lockedGold + .getDelegatorDelegateeExpectedAndRealAmount(_delegator, _delegatee); + assertEq(expectedAmount, expected); + assertEq(realAmount, real); + } + + function test_ShouldReturn0_WhenNothingDelegated() public { + assertDelegatorDelegateeExpectedAndRealAmount(delegator, delegatee, 0, 0); + } + + function test_ShouldReturnEqualAmounts_WhenDelegated() public { + delegateCelo(delegator, delegatee, percent); + assertDelegatorDelegateeExpectedAndRealAmount(delegator, delegatee, amount, amount); + } + + function helper_ShouldReturnEqualAmount( + address _delegator, + address _delegatorSigner, + address _delegatee + ) public { + uint256 updatedDelegatedAmount = ((value * 2) / 100) * percent; + + delegateCelo(_delegatorSigner, _delegatee, percent); + lockCelo(_delegator, value); + + assertDelegatorDelegateeExpectedAndRealAmount( + _delegator, + _delegatee, + updatedDelegatedAmount, + updatedDelegatedAmount + ); + + assertEq( + lockedGold.getAccountTotalGovernanceVotingPower(_delegatee), + updatedDelegatedAmount + value + ); + } + + function test_ShouldReturnEqualAmountAndUpdateTotalVotingPowerOfDelegatee_WhenMoreCeloLocked() + public + { + helper_ShouldReturnEqualAmount(delegator, delegator, delegatee); + } + + function test_ShouldReturnEqualAmounts_WhenDelegated_WhenVOteSigners() public { + whenVoteSigner_LockedGoldGetDelegatorDelegateeExpectedAndRealAmount(); + vm.prank(delegatorSigner); + lockedGold.delegateGovernanceVotes( + delegatee, + FixidityLib.newFixedFraction(percent, 100).unwrap() + ); + + assertDelegatorDelegateeExpectedAndRealAmount(delegator, delegatee, amount, amount); + } + + function test_ShouldReturnEqualAmountAndUpdateTotalVotingPowerOfDelegatee_WhenMoreCeloLocked_WhenVoteSigners() + public + { + whenVoteSigner_LockedGoldGetDelegatorDelegateeExpectedAndRealAmount(); + helper_ShouldReturnEqualAmount(delegator, delegatorSigner, delegatee); + + } +} + +contract LockedGoldUpdateDelegatedAmount is LockedGoldTest { + address delegator = actor("delegator"); + address delegatee = actor("delegatee"); + address delegatorSigner; + uint256 delegatorSignerPK; + address delegateeSigner; + uint256 delegateeSignerPK; + uint256 value = 1000; + uint256 delegatedPercent = 70; + uint256 delegatedAmount = (value / 100) * delegatedPercent; + + function whenVoteSigner_LockedGoldUpdateDelegatedAmount() public { + helper_WhenVoteSigners( + WhenVoteSignerStruct( + delegator, + address(0), + delegatee, + address(0), + delegatorSignerPK, + delegateeSignerPK, + 0, + 0, + false + ) + ); + } + + function setUp() public { + super.setUp(); + + vm.prank(delegator); + accounts.createAccount(); + vm.prank(delegatee); + accounts.createAccount(); + + vm.deal(delegator, 10 ether); + vm.deal(delegatee, 10 ether); + + vm.prank(delegator); + lockedGold.lock.value(value)(); + + (delegatorSigner, delegatorSignerPK) = actorWithPK("delegatorSigner"); + (delegateeSigner, delegateeSignerPK) = actorWithPK("delegateeSigner"); + } + + function helper_ShouldReturnCorrectValue( + address _delegator, + address _delegatorSigner, + address _delegatee + ) public { + delegateCelo(_delegatorSigner, _delegatee, delegatedPercent); + + assertEq(lockedGold.getAccountTotalGovernanceVotingPower(_delegatee), delegatedAmount); + assertDelegatorDelegateeAmounts(_delegator, _delegatee, delegatedPercent, delegatedAmount); + + lockCelo(_delegator, value); + lockedGold.updateDelegatedAmount(_delegator, _delegatee); + + assertEq(lockedGold.getAccountTotalLockedGold(_delegator), value * 2); + assertEq(lockedGold.getAccountTotalGovernanceVotingPower(_delegatee), delegatedAmount * 2); + assertDelegatorDelegateeAmounts(_delegator, _delegatee, delegatedPercent, delegatedAmount * 2); + } + + function test_ShouldReturnCorrectValueWhenLockedAndDelegated_WhenDelegatorLockedMoreCelo() + public + { + helper_ShouldReturnCorrectValue(delegator, delegator, delegatee); + } + + function test_ShouldReturnCorrectValueWhenLockedAndDelegated_WhenDelegatorLockedMoreCelo_WhenVoteSigners() + public + { + whenVoteSigner_LockedGoldUpdateDelegatedAmount(); + helper_ShouldReturnCorrectValue(delegator, delegatorSigner, delegatee); + } +} + +contract LockedGoldGetTotalPendingWithdrawalsCount is LockedGoldTest { + uint256 value = 1000; + address account = actor("account"); + + function setUp() public { + super.setUp(); + + vm.deal(account, 10 ether); + } + + function test_ShouldReturn0_WhenAccountHasNoPendingWithdrawals() public { + assertEq(lockedGold.getTotalPendingWithdrawalsCount(actor("account")), 0); + } + + function test_ShouldReturnCorrectValue_WhenAccountHasPendingWithdrawals() public { + vm.startPrank(account); + accounts.createAccount(); + lockedGold.lock.value(value)(); + + lockedGold.unlock(value / 2); + lockedGold.unlock(value / 2); + + assertEq(lockedGold.getTotalPendingWithdrawalsCount(account), 2); + } + + function test_ShouldReturn0_WhenNonExistentAccount() public { + assertEq(lockedGold.getTotalPendingWithdrawalsCount(randomAddress), 0); + } +} diff --git a/packages/protocol/test/governance/voting/lockedgold.ts b/packages/protocol/test/governance/voting/lockedgold.ts deleted file mode 100644 index 8d8b94564b1..00000000000 --- a/packages/protocol/test/governance/voting/lockedgold.ts +++ /dev/null @@ -1,2926 +0,0 @@ -import { NULL_ADDRESS } from '@celo/base/lib/address' -import { CeloContractName } from '@celo/protocol/lib/registry-utils' -import { - assertDelegatorDelegateeAmounts, - assertEqualBN, - assertLogMatches, - assertLogMatches2, - assertRevert, - assertTransactionRevertWithReason, - createAndAssertDelegatorDelegateeSigners, - timeTravel, -} from '@celo/protocol/lib/test-utils' -import { ZERO_ADDRESS } from '@celo/protocol/test/constants' -import { fromFixed, toFixed } from '@celo/utils/lib/fixidity' -import BigNumber from 'bignumber.js' -import { - AccountsContract, - AccountsInstance, - ElectionContract, - ElectionTestInstance, - LockedGoldContract, - LockedGoldInstance, - MockElectionContract, - MockElectionInstance, - MockGoldTokenContract, - MockGoldTokenInstance, - MockGovernanceContract, - MockGovernanceInstance, - MockValidatorsContract, - MockValidatorsInstance, - RegistryContract, - RegistryInstance, -} from 'types' - -const Accounts: AccountsContract = artifacts.require('Accounts') -const LockedGold: LockedGoldContract = artifacts.require('LockedGold') -const Election: ElectionContract = artifacts.require('Election') -const MockElection: MockElectionContract = artifacts.require('MockElection') -const MockGoldToken: MockGoldTokenContract = artifacts.require('MockGoldToken') -const MockGovernance: MockGovernanceContract = artifacts.require('MockGovernance') -const MockValidators: MockValidatorsContract = artifacts.require('MockValidators') -const Registry: RegistryContract = artifacts.require('Registry') - -// @ts-ignore -// TODO(mcortesi): Use BN -LockedGold.numberFormat = 'BigNumber' -// @ts-ignore -// TODO(mcortesi): Use BN -Election.numberFormat = 'BigNumber' - -const HOUR = 60 * 60 -const DAY = 24 * HOUR - -/* - * Helpers for verification - */ -export enum KeyOffsets { - VALIDATING_KEY_OFFSET, - ATTESTING_KEY_OFFSET, - NEW_VALIDATING_KEY_OFFSET, - VOTING_KEY_OFFSET, -} - -contract('LockedGold', (accounts: string[]) => { - const account = accounts[0] - const nonOwner = accounts[1] - const unlockingPeriod = 3 * DAY - let accountsInstance: AccountsInstance - let lockedGold: LockedGoldInstance - let election: ElectionTestInstance - let mockElection: MockElectionInstance - let mockGovernance: MockGovernanceInstance - let mockValidators: MockValidatorsInstance - let registry: RegistryInstance - let mockGoldToken: MockGoldTokenInstance - - beforeEach(async () => { - mockGoldToken = await MockGoldToken.new() - accountsInstance = await Accounts.new(true) - lockedGold = await LockedGold.new(true) - mockElection = await MockElection.new() - mockValidators = await MockValidators.new() - mockGovernance = await MockGovernance.new() - registry = await Registry.new(true) - await registry.setAddressFor(CeloContractName.Accounts, accountsInstance.address) - await registry.setAddressFor(CeloContractName.Election, mockElection.address) - await registry.setAddressFor(CeloContractName.GoldToken, mockGoldToken.address) - await registry.setAddressFor(CeloContractName.Governance, mockGovernance.address) - await registry.setAddressFor(CeloContractName.Validators, mockValidators.address) - await registry.setAddressFor(CeloContractName.LockedGold, lockedGold.address) - await lockedGold.initialize(registry.address, unlockingPeriod) - await accountsInstance.createAccount() - }) - - describe('#initialize()', () => { - it('should set the owner', async () => { - const owner: string = await lockedGold.owner() - assert.equal(owner, account) - }) - - it('should set the registry address', async () => { - const registryAddress: string = await lockedGold.registry() - assert.equal(registryAddress, registry.address) - }) - - it('should set the unlocking period', async () => { - const period = await lockedGold.unlockingPeriod() - assertEqualBN(unlockingPeriod, period) - }) - - it('should revert if already initialized', async () => { - await assertTransactionRevertWithReason( - lockedGold.initialize(registry.address, unlockingPeriod), - 'contract already initialized' - ) - }) - }) - - describe('#setRegistry()', () => { - const anAddress: string = accounts[2] - - it('should set the registry when called by the owner', async () => { - await lockedGold.setRegistry(anAddress) - assert.equal(await lockedGold.registry(), anAddress) - }) - - it('should revert when not called by the owner', async () => { - await assertTransactionRevertWithReason( - lockedGold.setRegistry(anAddress, { from: nonOwner }), - 'Ownable: caller is not the owner' - ) - }) - }) - - describe('#setUnlockingPeriod', () => { - const newUnlockingPeriod = unlockingPeriod + 1 - it('should set the unlockingPeriod', async () => { - await lockedGold.setUnlockingPeriod(newUnlockingPeriod) - assertEqualBN(await lockedGold.unlockingPeriod(), newUnlockingPeriod) - }) - - it('should emit the UnlockingPeriodSet event', async () => { - const resp = await lockedGold.setUnlockingPeriod(newUnlockingPeriod) - assert.equal(resp.logs.length, 1) - const log = resp.logs[0] - assertLogMatches2(log, { - event: 'UnlockingPeriodSet', - args: { - period: newUnlockingPeriod, - }, - }) - }) - - it('should revert when the unlockingPeriod is unchanged', async () => { - await assertTransactionRevertWithReason( - lockedGold.setUnlockingPeriod(unlockingPeriod), - 'Unlocking period not changed' - ) - }) - - it('should revert when called by anyone other than the owner', async () => { - await assertTransactionRevertWithReason( - lockedGold.setUnlockingPeriod(newUnlockingPeriod, { from: nonOwner }), - 'Ownable: caller is not the owner' - ) - }) - }) - - describe('#lock()', () => { - const value = 1000 - - it("should increase the account's nonvoting locked gold balance", async () => { - // @ts-ignore: TODO(mcortesi) fix typings for TransactionDetails - await lockedGold.lock({ value }) - assertEqualBN(await lockedGold.getAccountNonvotingLockedGold(account), value) - }) - - it("should increase the account's total locked gold balance", async () => { - // @ts-ignore: TODO(mcortesi) fix typings for TransactionDetails - await lockedGold.lock({ value }) - assertEqualBN(await lockedGold.getAccountTotalLockedGold(account), value) - }) - - it('should increase the nonvoting locked gold balance', async () => { - // @ts-ignore: TODO(mcortesi) fix typings for TransactionDetails - await lockedGold.lock({ value }) - assertEqualBN(await lockedGold.getNonvotingLockedGold(), value) - }) - - it('should increase the total locked gold balance', async () => { - // @ts-ignore: TODO(mcortesi) fix typings for TransactionDetails - await lockedGold.lock({ value }) - assertEqualBN(await lockedGold.getTotalLockedGold(), value) - }) - - it('should emit a GoldLocked event', async () => { - // @ts-ignore: TODO(mcortesi) fix typings for TransactionDetails - const resp = await lockedGold.lock({ value }) - assert.equal(resp.logs.length, 1) - const log = resp.logs[0] - assertLogMatches(log, 'GoldLocked', { - account, - value: new BigNumber(value), - }) - }) - - it('should revert when the account does not exist', async () => { - await assertTransactionRevertWithReason( - // @ts-ignore: TODO(mcortesi) fix typings for TransactionDetails - lockedGold.lock({ value, from: accounts[1] }), - 'Must first register address with Account.createAccount' - ) - }) - }) - - describe('#unlock()', () => { - const value = 1000 - let availabilityTime: BigNumber - let resp: Truffle.TransactionResponse - describe('when there are no balance requirements', () => { - beforeEach(async () => { - // @ts-ignore: TODO(mcortesi) fix typings for TransactionDetails - await lockedGold.lock({ value }) - }) - describe('when the account is not voting in governance', () => { - beforeEach(async () => { - resp = await lockedGold.unlock(value) - availabilityTime = new BigNumber(unlockingPeriod).plus( - (await web3.eth.getBlock('latest')).timestamp - ) - }) - - it('should add a pending withdrawal #getPendingWithdrawal()', async () => { - const [val, timestamp] = await lockedGold.getPendingWithdrawal(account, 0) - assertEqualBN(val, value) - assertEqualBN(timestamp, availabilityTime) - await assertRevert(lockedGold.getPendingWithdrawal(account, 1)) - }) - - it('should add a pending withdrawal #getPendingWithdrawals()', async () => { - const [values, timestamps] = await lockedGold.getPendingWithdrawals(account) - assert.equal(values.length, 1) - assert.equal(timestamps.length, 1) - assertEqualBN(values[0], value) - assertEqualBN(timestamps[0], availabilityTime) - }) - - it("should decrease the account's nonvoting locked gold balance", async () => { - assertEqualBN(await lockedGold.getAccountNonvotingLockedGold(account), 0) - }) - - it("should decrease the account's total locked gold balance", async () => { - assertEqualBN(await lockedGold.getAccountTotalLockedGold(account), 0) - }) - - it('should decrease the nonvoting locked gold balance', async () => { - assertEqualBN(await lockedGold.getNonvotingLockedGold(), 0) - }) - - it('should decrease the total locked gold balance', async () => { - assertEqualBN(await lockedGold.getTotalLockedGold(), 0) - }) - - it('should emit a GoldUnlocked event', async () => { - assert.equal(resp.logs.length, 1) - const log = resp.logs[0] - assertLogMatches(log, 'GoldUnlocked', { - account, - value: new BigNumber(value), - available: availabilityTime, - }) - }) - }) - - describe('when the account is voting in governance', () => { - const votingGold = 1 - const valueWithoutVotingGold = value - votingGold - beforeEach(async () => { - await mockGovernance.setVoting(account) - await mockGovernance.setTotalVotes(account, votingGold) - }) - - it('should revert when requesting gold that is voted with', async () => { - await assertTransactionRevertWithReason( - lockedGold.unlock(value), - 'Not enough unlockable celo. Celo is locked in voting.' - ) - }) - - describe('when the account is requesting only non voting gold', () => { - beforeEach(async () => { - resp = await lockedGold.unlock(valueWithoutVotingGold) - availabilityTime = new BigNumber(unlockingPeriod).plus( - (await web3.eth.getBlock('latest')).timestamp - ) - }) - - it('should add a pending withdrawal #getPendingWithdrawal()', async () => { - const [val, timestamp] = await lockedGold.getPendingWithdrawal(account, 0) - assertEqualBN(val, valueWithoutVotingGold) - assertEqualBN(timestamp, availabilityTime) - await assertRevert(lockedGold.getPendingWithdrawal(account, 1)) - }) - - it('should add a pending withdrawal #getPendingWithdrawals()', async () => { - const [values, timestamps] = await lockedGold.getPendingWithdrawals(account) - assert.equal(values.length, 1) - assert.equal(timestamps.length, 1) - assertEqualBN(values[0], valueWithoutVotingGold) - assertEqualBN(timestamps[0], availabilityTime) - }) - - it("should decrease the account's nonvoting locked gold balance", async () => { - assertEqualBN(await lockedGold.getAccountNonvotingLockedGold(account), votingGold) - }) - - it("should decrease the account's total locked gold balance", async () => { - assertEqualBN(await lockedGold.getAccountTotalLockedGold(account), votingGold) - }) - - it('should decrease the nonvoting locked gold balance', async () => { - assertEqualBN(await lockedGold.getNonvotingLockedGold(), votingGold) - }) - - it('should decrease the total locked gold balance', async () => { - assertEqualBN(await lockedGold.getTotalLockedGold(), votingGold) - }) - - it('should emit a GoldUnlocked event', async () => { - assert.equal(resp.logs.length, 1) - const log = resp.logs[0] - assertLogMatches(log, 'GoldUnlocked', { - account, - value: new BigNumber(valueWithoutVotingGold), - available: availabilityTime, - }) - }) - }) - }) - - describe('when the account is delegating', () => { - const delegatee = accounts[5] - const percentToDelagate = 50 - const toUnlock = Math.ceil((value / 100) * (100 - percentToDelagate)) // 500 - const originallyDelegatedAmount = (value / 100) * percentToDelagate // 500 - - beforeEach(async () => { - await accountsInstance.createAccount({ from: delegatee }) - await lockedGold.delegateGovernanceVotes(delegatee, toFixed(percentToDelagate / 100)) - await mockGovernance.setTotalVotes(delegatee, originallyDelegatedAmount) - await lockedGold.unlock(toUnlock) - }) - - it('should correctly unlock when getting less or equal to locked amount', async () => { - const [val] = await lockedGold.getPendingWithdrawal(account, 0) - assertEqualBN(val, toUnlock) - }) - - it('should correctly update delegated amount for delegatee', async () => { - const [expected, real] = await lockedGold.getDelegatorDelegateeExpectedAndRealAmount( - accounts[0], - delegatee - ) - assertEqualBN(expected, originallyDelegatedAmount / 2) - assertEqualBN(real, toUnlock / 2) - }) - - it('should decrease total delegated amount for delegatee', async () => { - const delegateeTotalAmount = await lockedGold.totalDelegatedCelo(delegatee) - assertEqualBN(delegateeTotalAmount, originallyDelegatedAmount / 2) - }) - - it('should call removeDelegatedVotes because voting for governance', async () => { - const removeDelegatedVotes = await mockGovernance.removeVotesCalledFor(delegatee) - assertEqualBN(removeDelegatedVotes, originallyDelegatedAmount / 2) - }) - - it('should correctly update delegator delegatee amount', async () => { - const [fraction, currentAmount] = await lockedGold.getDelegatorDelegateeInfo( - accounts[0], - delegatee - ) - assertEqualBN(fromFixed(fraction.multipliedBy(100)), percentToDelagate) - assertEqualBN(currentAmount, originallyDelegatedAmount / 2) - }) - - describe('When all is unlocked', () => { - beforeEach(async () => { - await mockGovernance.setTotalVotes(delegatee, originallyDelegatedAmount / 2) - await lockedGold.unlock(toUnlock) - }) - - it('should not remove delegatee from queue', async () => { - const delegatees = await lockedGold.getDelegateesOfDelegator(accounts[0]) - assert.sameDeepMembers([delegatee], delegatees) - }) - - it('should correctly update delegator delegatee amount', async () => { - const [fraction, currentAmount] = await lockedGold.getDelegatorDelegateeInfo( - accounts[0], - delegatee - ) - assertEqualBN(fromFixed(fraction.multipliedBy(100)), percentToDelagate) - assertEqualBN(currentAmount, 0) - }) - }) - }) - - describe('when the account is delegating to 2 delegatees', () => { - const delegatee = accounts[5] - const delegatee2 = accounts[6] - const percentToDelagate = 50 - const toUnlock = Math.ceil((value / 100) * (100 - percentToDelagate)) + 1 // 501 - const originallyDelegatedAmount = (value / 100) * percentToDelagate // 500 - - beforeEach(async () => { - // @ts-ignore: TODO(mcortesi) fix typings for TransactionDetails - await lockedGold.lock({ value: 1 }) - await accountsInstance.createAccount({ from: delegatee }) - await accountsInstance.createAccount({ from: delegatee2 }) - await lockedGold.delegateGovernanceVotes(delegatee, toFixed(percentToDelagate / 100)) - await lockedGold.delegateGovernanceVotes(delegatee2, toFixed(percentToDelagate / 100)) - await mockGovernance.setTotalVotes(delegatee, originallyDelegatedAmount) - await mockGovernance.setTotalVotes(delegatee2, originallyDelegatedAmount) - await lockedGold.unlock(toUnlock) - }) - - it('should correctly unlock when getting less or equal to locked amount', async () => { - const [val] = await lockedGold.getPendingWithdrawal(account, 0) - assertEqualBN(val, toUnlock) - }) - - it('should correctly update delegated amount for delegatee', async () => { - const [expected, real] = await lockedGold.getDelegatorDelegateeExpectedAndRealAmount( - accounts[0], - delegatee - ) - assertEqualBN(expected, originallyDelegatedAmount / 2) - assertEqualBN(real, Math.floor(toUnlock / 2)) - - const [expected2, real2] = await lockedGold.getDelegatorDelegateeExpectedAndRealAmount( - accounts[0], - delegatee2 - ) - assertEqualBN(expected2, originallyDelegatedAmount / 2) - assertEqualBN(real2, Math.floor(toUnlock / 2)) - }) - - it('should decrease total delegated amount for delegatee', async () => { - const delegateeTotalAmount = await lockedGold.totalDelegatedCelo(delegatee) - assertEqualBN(delegateeTotalAmount, originallyDelegatedAmount / 2) - - const delegateeTotalAmount2 = await lockedGold.totalDelegatedCelo(delegatee2) - assertEqualBN(delegateeTotalAmount2, originallyDelegatedAmount / 2) - }) - - it('should call removeDelegatedVotes because voting for governance', async () => { - const removeDelegatedVotes = await mockGovernance.removeVotesCalledFor(delegatee) - assertEqualBN(removeDelegatedVotes, originallyDelegatedAmount / 2) - - const removeDelegatedVotes2 = await mockGovernance.removeVotesCalledFor(delegatee2) - assertEqualBN(removeDelegatedVotes2, originallyDelegatedAmount / 2) - }) - }) - - describe('when the account is delegating to 3 delegatees', () => { - const delegatee = accounts[5] - const delegatee2 = accounts[6] - const delegatee3 = accounts[7] - const percentToDelagate = 33 - const toUnlock = 4 - - beforeEach(async () => { - // @ts-ignore: TODO(mcortesi) fix typings for TransactionDetails - await lockedGold.unlock(995) // I have only 5 locked now - await accountsInstance.createAccount({ from: delegatee }) - await accountsInstance.createAccount({ from: delegatee2 }) - await accountsInstance.createAccount({ from: delegatee3 }) - await lockedGold.delegateGovernanceVotes(delegatee, toFixed(percentToDelagate / 100)) - await lockedGold.delegateGovernanceVotes(delegatee2, toFixed(percentToDelagate / 100)) - await lockedGold.delegateGovernanceVotes(delegatee3, toFixed(percentToDelagate / 100)) - - const [expected, real] = await lockedGold.getDelegatorDelegateeExpectedAndRealAmount( - accounts[0], - delegatee - ) - assertEqualBN(expected, 1) - assertEqualBN(real, 1) - - const [expected2, real2] = await lockedGold.getDelegatorDelegateeExpectedAndRealAmount( - accounts[0], - delegatee2 - ) - assertEqualBN(expected2, 1) - assertEqualBN(real2, 1) - - const [expected3, real3] = await lockedGold.getDelegatorDelegateeExpectedAndRealAmount( - accounts[0], - delegatee3 - ) - assertEqualBN(expected3, 1) - assertEqualBN(real3, 1) - await mockGovernance.setTotalVotes(delegatee, 1) - await mockGovernance.setTotalVotes(delegatee2, 1) - await mockGovernance.setTotalVotes(delegatee3, 1) - - await mockGovernance.removeVotesWhenRevokingDelegatedVotes(delegatee, 9999) - await mockGovernance.removeVotesWhenRevokingDelegatedVotes(delegatee2, 9999) - await mockGovernance.removeVotesWhenRevokingDelegatedVotes(delegatee3, 9999) - - resp = await lockedGold.unlock(toUnlock) - }) - - it('should correctly unlock when getting less or equal to locked amount', async () => { - const [val] = await lockedGold.getPendingWithdrawal(account, 1) - assertEqualBN(val, toUnlock) - }) - - it('should correctly update delegated amount for delegatee', async () => { - const [expected, real] = await lockedGold.getDelegatorDelegateeExpectedAndRealAmount( - accounts[0], - delegatee - ) - assertEqualBN(expected, 0) - assertEqualBN(real, 0) - - const [expected2, real2] = await lockedGold.getDelegatorDelegateeExpectedAndRealAmount( - accounts[0], - delegatee2 - ) - assertEqualBN(expected2, 0) - assertEqualBN(real2, 0) - - const [expected3, real3] = await lockedGold.getDelegatorDelegateeExpectedAndRealAmount( - accounts[0], - delegatee3 - ) - assertEqualBN(expected3, 0) - assertEqualBN(real3, 0) - }) - - it('should decrease total delegated amount for delegatee', async () => { - const delegateeTotalAmount = await lockedGold.totalDelegatedCelo(delegatee) - assertEqualBN(delegateeTotalAmount, 0) - - const delegateeTotalAmount2 = await lockedGold.totalDelegatedCelo(delegatee2) - assertEqualBN(delegateeTotalAmount2, 0) - }) - - it('should call removeDelegatedVotes because voting for governance', async () => { - const removeDelegatedVotes = await mockGovernance.removeVotesCalledFor(delegatee) - assertEqualBN(removeDelegatedVotes, 0) - - const removeDelegatedVotes2 = await mockGovernance.removeVotesCalledFor(delegatee2) - assertEqualBN(removeDelegatedVotes2, 0) - - const removeDelegatedVotes3 = await mockGovernance.removeVotesCalledFor(delegatee3) - assertEqualBN(removeDelegatedVotes3, 0) - }) - - it('should emit the DelegatedCeloRevoked event', async () => { - assert.equal(resp.logs.length, 4) - assertLogMatches2(resp.logs[0], { - event: 'DelegatedCeloRevoked', - args: { - delegator: accounts[0], - delegatee, - percent: 0, - amount: 1, - }, - }) - assertLogMatches2(resp.logs[1], { - event: 'DelegatedCeloRevoked', - args: { - delegator: accounts[0], - delegatee: delegatee2, - percent: 0, - amount: 1, - }, - }) - assertLogMatches2(resp.logs[2], { - event: 'DelegatedCeloRevoked', - args: { - delegator: accounts[0], - delegatee: delegatee3, - percent: 0, - amount: 1, - }, - }) - }) - }) - }) - - describe('when there are balance requirements', () => { - const balanceRequirement = 10 - beforeEach(async () => { - // @ts-ignore: TODO(mcortesi) fix typings for TransactionDetails - await lockedGold.lock({ value }) - await mockValidators.setAccountLockedGoldRequirement(account, balanceRequirement) - }) - - describe('when unlocking would yield a locked gold balance less than the required value', () => { - describe('when the the current time is earlier than the requirement time', () => { - it('should revert', async () => { - await assertTransactionRevertWithReason( - lockedGold.unlock(value), - "Either account doesn't have enough locked Celo or locked Celo is being used for voting." - ) - }) - }) - }) - - describe('when unlocking would yield a locked gold balance equal to the required value', () => { - it('should succeed', async () => { - await lockedGold.unlock(value - balanceRequirement) - }) - }) - }) - }) - - describe('#relock()', () => { - const pendingWithdrawalValue = 1000 - const index = 0 - let resp: Truffle.TransactionResponse - describe('when a pending withdrawal exists', () => { - beforeEach(async () => { - // @ts-ignore: TODO(mcortesi) fix typings for TransactionDetails - await lockedGold.lock({ value: pendingWithdrawalValue }) - await lockedGold.unlock(pendingWithdrawalValue) - }) - - describe('when relocking value equal to the value of the pending withdrawal', () => { - const value = pendingWithdrawalValue - beforeEach(async () => { - resp = await lockedGold.relock(index, value) - }) - - it("should increase the account's nonvoting locked gold balance", async () => { - assertEqualBN(await lockedGold.getAccountNonvotingLockedGold(account), value) - }) - - it("should increase the account's total locked gold balance", async () => { - assertEqualBN(await lockedGold.getAccountTotalLockedGold(account), value) - }) - - it('should increase the nonvoting locked gold balance', async () => { - assertEqualBN(await lockedGold.getNonvotingLockedGold(), value) - }) - - it('should increase the total locked gold balance', async () => { - assertEqualBN(await lockedGold.getTotalLockedGold(), value) - }) - - it('should emit a GoldRelocked event', async () => { - assert.equal(resp.logs.length, 1) - const log = resp.logs[0] - assertLogMatches(log, 'GoldRelocked', { - account, - value: new BigNumber(value), - }) - }) - - it('should remove the pending withdrawal', async () => { - const [values, timestamps] = await lockedGold.getPendingWithdrawals(account) - assert.equal(values.length, 0) - assert.equal(timestamps.length, 0) - }) - }) - - describe('when relocking value less than the value of the pending withdrawal', () => { - const value = pendingWithdrawalValue - 1 - beforeEach(async () => { - resp = await lockedGold.relock(index, value) - }) - - it("should increase the account's nonvoting locked gold balance", async () => { - assertEqualBN(await lockedGold.getAccountNonvotingLockedGold(account), value) - }) - - it("should increase the account's total locked gold balance", async () => { - assertEqualBN(await lockedGold.getAccountTotalLockedGold(account), value) - }) - - it('should increase the nonvoting locked gold balance', async () => { - assertEqualBN(await lockedGold.getNonvotingLockedGold(), value) - }) - - it('should increase the total locked gold balance', async () => { - assertEqualBN(await lockedGold.getTotalLockedGold(), value) - }) - - it('should emit a GoldRelocked event', async () => { - assert.equal(resp.logs.length, 1) - const log = resp.logs[0] - assertLogMatches(log, 'GoldRelocked', { - account, - value: new BigNumber(value), - }) - }) - - it('should decrement the value of the pending withdrawal', async () => { - const [values, timestamps] = await lockedGold.getPendingWithdrawals(account) - assert.equal(values.length, 1) - assert.equal(timestamps.length, 1) - assertEqualBN(values[0], 1) - }) - }) - describe('when relocking value greater than the value of the pending withdrawal', () => { - const value = pendingWithdrawalValue + 1 - it('should revert', async () => { - await assertTransactionRevertWithReason( - lockedGold.relock(index, value), - 'Requested value larger than pending value' - ) - }) - }) - }) - - describe('When delegating', () => { - const delegatee = accounts[5] - - beforeEach(async () => { - // @ts-ignore: TODO(mcortesi) fix typings for TransactionDetails - await lockedGold.lock({ value: pendingWithdrawalValue }) - await accountsInstance.createAccount({ from: delegatee }) - await lockedGold.delegateGovernanceVotes(delegatee, toFixed(100 / 100)) - await lockedGold.unlock(pendingWithdrawalValue / 2) - const [expected, real] = await lockedGold.getDelegatorDelegateeExpectedAndRealAmount( - accounts[0], - delegatee - ) - assertEqualBN(expected, pendingWithdrawalValue / 2) - assertEqualBN(real, pendingWithdrawalValue / 2) - }) - - it('should update delegated amount', async () => { - await lockedGold.relock(index, pendingWithdrawalValue / 2) - const [expected, real] = await lockedGold.getDelegatorDelegateeExpectedAndRealAmount( - accounts[0], - delegatee - ) - assertEqualBN(expected, pendingWithdrawalValue) - assertEqualBN(real, pendingWithdrawalValue) - }) - }) - - describe('when a pending withdrawal does not exist', () => { - it('should revert', async () => { - await assertTransactionRevertWithReason( - lockedGold.relock(index, pendingWithdrawalValue), - 'Bad pending withdrawal index' - ) - }) - }) - }) - - describe('#withdraw()', () => { - const value = 1000 - const index = 0 - let resp: Truffle.TransactionResponse - describe('when a pending withdrawal exists', () => { - beforeEach(async () => { - // @ts-ignore: TODO(mcortesi) fix typings for TransactionDetails - await lockedGold.lock({ value }) - resp = await lockedGold.unlock(value) - }) - - describe('when it is after the availablity time', () => { - beforeEach(async () => { - await timeTravel(unlockingPeriod, web3) - resp = await lockedGold.withdraw(index) - }) - - it('should remove the pending withdrawal', async () => { - const [values, timestamps] = await lockedGold.getPendingWithdrawals(account) - assert.equal(values.length, 0) - assert.equal(timestamps.length, 0) - }) - - it('should emit a GoldWithdrawn event', async () => { - assert.equal(resp.logs.length, 1) - const log = resp.logs[0] - assertLogMatches(log, 'GoldWithdrawn', { - account, - value: new BigNumber(value), - }) - }) - }) - - describe('when it is before the availablity time', () => { - it('should revert', async () => { - await assertTransactionRevertWithReason( - lockedGold.withdraw(index), - 'Pending withdrawal not available' - ) - }) - }) - }) - - describe('when a pending withdrawal does not exist', () => { - it('should revert', async () => { - await assertTransactionRevertWithReason( - lockedGold.withdraw(index), - 'Bad pending withdrawal index' - ) - }) - }) - }) - - describe('#addSlasher', () => { - beforeEach(async () => { - await registry.setAddressFor(CeloContractName.DowntimeSlasher, accounts[2]) - }) - it('can add slasher to whitelist', async () => { - await lockedGold.addSlasher(CeloContractName.DowntimeSlasher) - const bytes = web3.utils.soliditySha3({ - type: 'string', - value: CeloContractName.DowntimeSlasher, - }) - assert.equal(bytes, (await lockedGold.getSlashingWhitelist())[0]) - }) - it('can only be called by owner', async () => { - await assertTransactionRevertWithReason( - lockedGold.addSlasher(CeloContractName.DowntimeSlasher, { from: accounts[1] }), - 'Ownable: caller is not the owner' - ) - }) - it('cannot add a slasher twice', async () => { - await lockedGold.addSlasher(CeloContractName.DowntimeSlasher) - await assertTransactionRevertWithReason( - lockedGold.addSlasher(CeloContractName.DowntimeSlasher), - 'Cannot add slasher ID twice.' - ) - }) - }) - - describe('#removeSlasher', () => { - beforeEach(async () => { - await registry.setAddressFor(CeloContractName.DowntimeSlasher, accounts[2]) - await registry.setAddressFor(CeloContractName.GovernanceSlasher, accounts[3]) - await lockedGold.addSlasher(CeloContractName.DowntimeSlasher) - }) - it('removes item for whitelist', async () => { - await lockedGold.removeSlasher(CeloContractName.DowntimeSlasher, 0) - assert.equal(0, (await lockedGold.getSlashingWhitelist()).length) - }) - it('can only be called by owner', async () => { - await assertTransactionRevertWithReason( - lockedGold.removeSlasher(CeloContractName.DowntimeSlasher, 0, { from: accounts[1] }), - 'Ownable: caller is not the owner' - ) - }) - it('reverts when index too large', async () => { - await assertTransactionRevertWithReason( - lockedGold.removeSlasher(CeloContractName.DowntimeSlasher, 100), - 'Provided index exceeds whitelist bounds.' - ) - }) - it('reverts when key does not exists', async () => { - await assertTransactionRevertWithReason( - lockedGold.removeSlasher(CeloContractName.GovernanceSlasher, 100), - 'Cannot remove slasher ID not yet added.' - ) - }) - it('reverts when index and key have mismatch', async () => { - await lockedGold.addSlasher(CeloContractName.GovernanceSlasher) - await assertTransactionRevertWithReason( - lockedGold.removeSlasher(CeloContractName.DowntimeSlasher, 1), - "Index doesn't match identifier" - ) - }) - }) - - describe('#slash', () => { - const value = 1000 - const group = accounts[1] - const reporter = accounts[3] - - beforeEach(async () => { - election = await Election.new(true) - await registry.setAddressFor(CeloContractName.LockedGold, lockedGold.address) - await election.initialize( - registry.address, - new BigNumber(4), - new BigNumber(6), - new BigNumber(3), - toFixed(1 / 100) - ) - await mockValidators.setMembers(group, [accounts[9]]) - await registry.setAddressFor(CeloContractName.Validators, accounts[0]) - await registry.setAddressFor(CeloContractName.Election, election.address) - await election.markGroupEligible(group, NULL_ADDRESS, NULL_ADDRESS) - await registry.setAddressFor(CeloContractName.Validators, mockValidators.address) - await mockValidators.setNumRegisteredValidators(1) - // @ts-ignore: TODO(mcortesi) fix typings for TransactionDetails - await lockedGold.lock({ value }) - await registry.setAddressFor(CeloContractName.DowntimeSlasher, accounts[2]) - await lockedGold.addSlasher(CeloContractName.DowntimeSlasher) - await accountsInstance.createAccount({ from: reporter }) - }) - - describe('when the account is slashed for all of its locked gold', () => { - const penalty = value - const reward = value / 2 - - beforeEach(async () => { - await lockedGold.slash( - account, - penalty, - reporter, - reward, - [NULL_ADDRESS], - [NULL_ADDRESS], - [0], - { from: accounts[2] } - ) - }) - - it("should reduce account's locked gold balance", async () => { - assertEqualBN(await lockedGold.getAccountNonvotingLockedGold(account), value - penalty) - assertEqualBN(await lockedGold.getAccountTotalLockedGold(account), value - penalty) - }) - - it("should increase the reporter's locked gold", async () => { - assertEqualBN(await lockedGold.getAccountNonvotingLockedGold(reporter), reward) - assertEqualBN(await lockedGold.getAccountTotalLockedGold(reporter), reward) - }) - - it("should increase the community fund's gold", async () => { - assert.equal(await web3.eth.getBalance(mockGovernance.address), penalty - reward) - }) - }) - - describe('When account is delegating', () => { - const penalty = value - const reward = value / 2 - - const delegatee = accounts[5] - - beforeEach(async () => { - await accountsInstance.createAccount({ from: delegatee }) - await lockedGold.delegateGovernanceVotes(delegatee, toFixed(100 / 100)) - }) - - it('should update total voting power of delegatee', async () => { - const totalAccountGovernanceVotingPower = - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee) - assertEqualBN(totalAccountGovernanceVotingPower, value) - }) - - describe('When slashed', () => { - beforeEach(async () => { - await lockedGold.slash( - account, - penalty, - reporter, - reward, - [NULL_ADDRESS], - [NULL_ADDRESS], - [0], - { from: accounts[2] } - ) - }) - - it("should reduce account's locked gold balance", async () => { - assertEqualBN(await lockedGold.getAccountNonvotingLockedGold(account), value - penalty) - assertEqualBN(await lockedGold.getAccountTotalLockedGold(account), value - penalty) - }) - }) - }) - - describe('when the slashing contract is removed from `isSlasher`', () => { - const penalty = value - const reward = value / 2 - beforeEach(async () => { - await lockedGold.removeSlasher(CeloContractName.DowntimeSlasher, 0) - }) - - it('should revert', async () => { - await assertTransactionRevertWithReason( - lockedGold.slash( - account, - penalty, - reporter, - reward, - [NULL_ADDRESS], - [NULL_ADDRESS], - [0], - { from: accounts[2] } - ), - 'Caller is not a whitelisted slasher.' - ) - }) - }) - - describe('when the account has half voting and half nonvoting gold', () => { - const voting = value / 2 - const nonVoting = value - voting - beforeEach(async () => { - await election.vote(group, voting, NULL_ADDRESS, NULL_ADDRESS) - }) - - describe('when the account is slashed for only its nonvoting balance', () => { - const penalty = nonVoting - const reward = penalty / 2 - beforeEach(async () => { - await lockedGold.slash( - account, - penalty, - reporter, - reward, - [NULL_ADDRESS], - [NULL_ADDRESS], - [0], - { from: accounts[2] } - ) - }) - - it("should reduce account's nonvoting locked gold balance", async () => { - assertEqualBN( - await lockedGold.getAccountNonvotingLockedGold(account), - nonVoting - penalty - ) - }) - - it('should leave the voting locked gold', async () => { - assertEqualBN(await lockedGold.getAccountTotalLockedGold(account), value - penalty) - assertEqualBN(await election.getTotalVotesByAccount(account), voting) - }) - - it("should increase the reporter's locked gold", async () => { - assertEqualBN(await lockedGold.getAccountNonvotingLockedGold(reporter), reward) - assertEqualBN(await lockedGold.getAccountTotalLockedGold(reporter), reward) - }) - - it("should increase the community fund's gold", async () => { - assert.equal(await web3.eth.getBalance(mockGovernance.address), penalty - reward) - }) - }) - - describe('when the account is slashed for its whole balance', () => { - const penalty = value - const reward = penalty / 2 - - beforeEach(async () => { - await lockedGold.slash( - account, - penalty, - reporter, - reward, - [NULL_ADDRESS], - [NULL_ADDRESS], - [0], - { from: accounts[2] } - ) - }) - - it("should reduce account's nonvoting locked gold balance", async () => { - assertEqualBN(await lockedGold.getAccountNonvotingLockedGold(account), value - penalty) - }) - - it("should reduce account's locked gold and voting gold", async () => { - assertEqualBN(await lockedGold.getAccountTotalLockedGold(account), value - penalty) - assertEqualBN(await election.getTotalVotesByAccount(account), value - penalty) - }) - - it("should increase the reporter's locked gold", async () => { - assertEqualBN(await lockedGold.getAccountNonvotingLockedGold(reporter), reward) - assertEqualBN(await lockedGold.getAccountTotalLockedGold(reporter), reward) - }) - - it("should increase the community fund's gold", async () => { - assert.equal(await web3.eth.getBalance(mockGovernance.address), penalty - reward) - }) - }) - - describe('when the account is slashed for more than its whole balance', () => { - const penalty = value * 2 - const reward = value / 2 - - beforeEach(async () => { - await lockedGold.slash( - account, - penalty, - reporter, - reward, - [NULL_ADDRESS], - [NULL_ADDRESS], - [0], - { from: accounts[2] } - ) - }) - - it('should slash the whole accounts balance', async () => { - assertEqualBN(await lockedGold.getAccountNonvotingLockedGold(account), 0) - assertEqualBN(await lockedGold.getAccountTotalLockedGold(account), 0) - assertEqualBN(await election.getTotalVotesByAccount(account), 0) - }) - - it('should still send the `reporter` `reward` gold', async () => { - assertEqualBN(await lockedGold.getAccountNonvotingLockedGold(reporter), reward) - assertEqualBN(await lockedGold.getAccountTotalLockedGold(reporter), reward) - }) - - it("should only send the community fund value based on `account`'s total balance", async () => { - assert.equal(await web3.eth.getBalance(mockGovernance.address), value - reward) - }) - }) - }) - - it('cannot be invoked by non-account reporters', async () => { - const penalty = value - const reward = value / 2 - - await assertTransactionRevertWithReason( - lockedGold.slash( - account, - penalty, - accounts[4], - reward, - [NULL_ADDRESS], - [NULL_ADDRESS], - [0], - { from: accounts[2] } - ), - 'Must first register address with Account.createAccount' - ) - }) - - it('can be invoked by an account signer on behalf of the account', async () => { - const signerReporter = accounts[4] - const role = '0x0000000000000000000000000000000000000000000000000000000000001337' - await accountsInstance.authorizeSigner(signerReporter, role, { from: reporter }) - await accountsInstance.completeSignerAuthorization(reporter, role, { from: signerReporter }) - const penalty = value - const reward = value / 2 - - await lockedGold.slash( - account, - penalty, - signerReporter, - reward, - [NULL_ADDRESS], - [NULL_ADDRESS], - [0], - { from: accounts[2] } - ) - - assertEqualBN(await lockedGold.getAccountNonvotingLockedGold(reporter), reward) - assertEqualBN(await lockedGold.getAccountTotalLockedGold(reporter), reward) - }) - }) - - describe('#delegateGovernanceVotes', () => { - it('should revert when delegatee is not account', async () => { - await assertTransactionRevertWithReason( - lockedGold.delegateGovernanceVotes(ZERO_ADDRESS, toFixed(10 / 100)), - 'Must first register address with Account.createAccount' - ) - }) - - it('should revert when delegator is not an account', async () => { - await assertTransactionRevertWithReason( - lockedGold.delegateGovernanceVotes(ZERO_ADDRESS, toFixed(10 / 100), { from: accounts[1] }), - 'Must first register address with Account.createAccount' - ) - }) - - describe('When delegatee is an account', () => { - const delegatee1 = accounts[5] - const delegatee2 = accounts[6] - const delegatee3 = accounts[7] - const delegator = accounts[0] - const delegator2 = accounts[1] - - beforeEach(async () => { - await accountsInstance.createAccount({ from: delegatee1 }) - await accountsInstance.createAccount({ from: delegatee2 }) - await accountsInstance.createAccount({ from: delegatee3 }) - await accountsInstance.createAccount({ from: delegator2 }) - }) - - describe('When no vote signers', () => { - describe('When no gold is locked', () => { - let resp: Truffle.TransactionResponse - const percentsToDelegate = 30 - const delegatedAmount = 0 - - beforeEach(async () => { - resp = await lockedGold.delegateGovernanceVotes( - delegatee1, - toFixed(percentsToDelegate / 100) - ) - }) - - it('should return correct delegated amounts', async () => { - await assertDelegatorDelegateeAmounts( - delegator, - delegatee1, - percentsToDelegate, - delegatedAmount, - lockedGold - ) - assertEqualBN( - fromFixed(await lockedGold.getAccountTotalDelegatedFraction(delegator)).multipliedBy( - 100 - ), - percentsToDelegate - ) - }) - - it('should emit the CeloDelegated event', async () => { - assert.equal(resp.logs.length, 1) - const log = resp.logs[0] - assertLogMatches2(log, { - event: 'CeloDelegated', - args: { - delegator, - delegatee: delegatee1, - percent: toFixed(percentsToDelegate / 100), - amount: delegatedAmount, - }, - }) - }) - }) - - describe('When some gold is locked', () => { - const value = 1000 - - beforeEach(async () => { - await lockedGold.lock({ value: value.toString(), from: delegator }) - await lockedGold.lock({ value: value.toString(), from: delegator2 }) - }) - - it('should revert when delegating as validator', async () => { - await mockValidators.setValidator(accounts[0]) - await assertTransactionRevertWithReason( - lockedGold.delegateGovernanceVotes(delegatee1, toFixed(10 / 100)), - 'Validators cannot delegate votes.' - ) - }) - - it('should revert when delegating as validator group', async () => { - await mockValidators.setValidatorGroup(accounts[0]) - await assertTransactionRevertWithReason( - lockedGold.delegateGovernanceVotes(delegatee1, 10), - 'Validator groups cannot delegate votes.' - ) - }) - - describe('When delegatee account is registered', () => { - const percentsToDelegate = 10 - const delegatedAmount = (value / 100) * percentsToDelegate - beforeEach(async () => { - assertEqualBN(await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), 0) - assertEqualBN(await lockedGold.totalDelegatedCelo(delegatee1), 0) - assertEqualBN(await lockedGold.getAccountTotalGovernanceVotingPower(delegatee2), 0) - assertEqualBN(await lockedGold.totalDelegatedCelo(delegator), 0) - assertEqualBN(await lockedGold.getAccountTotalDelegatedFraction(delegator), 0) - }) - - it('should revert when incorrect percent amount is inserted', async () => { - await assertTransactionRevertWithReason( - lockedGold.delegateGovernanceVotes(ZERO_ADDRESS, toFixed(101 / 100)), - 'Delegate fraction must be less than or equal to 1' - ) - }) - - describe('When delegator is voting in referendum', () => { - beforeEach(async () => { - await mockGovernance.setTotalVotes(delegator, 1) - }) - - it('should revert when delagating votes that are currently voting for proposal', async () => { - await assertTransactionRevertWithReason( - lockedGold.delegateGovernanceVotes(delegatee1, toFixed(100 / 100)), - 'Cannot delegate votes that are voting in referendum' - ) - }) - - it('should revert when voting for proposal with votes that are currently used in referendum (2 delegatees)', async () => { - await lockedGold.delegateGovernanceVotes(delegatee1, toFixed(99 / 100)) - await assertTransactionRevertWithReason( - lockedGold.delegateGovernanceVotes(delegatee2, toFixed(1 / 100)), - 'Cannot delegate votes that are voting in referendum' - ) - }) - - it('should delegate when voting for less than requested for delegation', async () => { - await lockedGold.delegateGovernanceVotes(delegatee1, toFixed(99 / 100)) - }) - }) - - describe('When delegating to delegatee1', () => { - let resp: Truffle.TransactionResponse - beforeEach(async () => { - resp = await lockedGold.delegateGovernanceVotes( - delegatee1, - toFixed(percentsToDelegate / 100) - ) - }) - it('should revert when delegating more than 100% in two steps (different delegatees)', async () => { - await assertTransactionRevertWithReason( - lockedGold.delegateGovernanceVotes(delegatee2, toFixed(100 / 100)), - 'Cannot delegate more than 100%' - ) - }) - - it('should delegate correctly when delegated to same account in two steps', async () => { - assertEqualBN(await lockedGold.totalDelegatedCelo(delegatee1), delegatedAmount) - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), - delegatedAmount - ) - assertEqualBN( - fromFixed( - await lockedGold.getAccountTotalDelegatedFraction(delegator) - ).multipliedBy(100), - percentsToDelegate - ) - await lockedGold.delegateGovernanceVotes(delegatee1, toFixed(100 / 100)) - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), - value - ) - await assertDelegatorDelegateeAmounts(delegator, delegatee1, 100, value, lockedGold) - assertEqualBN( - fromFixed( - await lockedGold.getAccountTotalDelegatedFraction(delegator) - ).multipliedBy(100), - 100 - ) - }) - - it('should emit the CeloDelegated event', async () => { - assert.equal(resp.logs.length, 1) - const log = resp.logs[0] - assertLogMatches2(log, { - event: 'CeloDelegated', - args: { - delegator, - delegatee: delegatee1, - percent: toFixed(percentsToDelegate / 100), - amount: delegatedAmount, - }, - }) - }) - - it('should delegate votes correctly', async () => { - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), - delegatedAmount - ) - await assertDelegatorDelegateeAmounts( - delegator, - delegatee1, - percentsToDelegate, - delegatedAmount, - lockedGold - ) - }) - - it('should delegate votes correctly to multiple accounts', async () => { - await lockedGold.delegateGovernanceVotes( - delegatee2, - toFixed(percentsToDelegate / 100) - ) - - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), - delegatedAmount - ) - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee2), - delegatedAmount - ) - - await assertDelegatorDelegateeAmounts( - delegator, - delegatee1, - percentsToDelegate, - delegatedAmount, - lockedGold - ) - await assertDelegatorDelegateeAmounts( - delegator, - delegatee2, - percentsToDelegate, - delegatedAmount, - lockedGold - ) - }) - - describe('When locked more gold and redelagate', () => { - let resp2: Truffle.TransactionResponse - beforeEach(async () => { - await lockedGold.lock({ value: value.toString(), from: delegator }) - resp2 = await lockedGold.delegateGovernanceVotes( - delegatee1, - toFixed(percentsToDelegate / 100) - ) - }) - - it('should delegate votes correctly', async () => { - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), - delegatedAmount * 2 - ) - await assertDelegatorDelegateeAmounts( - delegator, - delegatee1, - percentsToDelegate, - delegatedAmount * 2, - lockedGold - ) - }) - - it('should emit the CeloDelegated event', async () => { - assert.equal(resp2.logs.length, 1) - const log = resp2.logs[0] - assertLogMatches2(log, { - event: 'CeloDelegated', - args: { - delegator, - delegatee: delegatee1, - percent: toFixed(percentsToDelegate / 100), - amount: delegatedAmount * 2, - }, - }) - }) - }) - }) - - describe('When 2 delegators are delegating to delegatee1', () => { - beforeEach(async () => { - await lockedGold.delegateGovernanceVotes( - delegatee1, - toFixed(percentsToDelegate / 100), - { - from: delegator, - } - ) - await lockedGold.delegateGovernanceVotes( - delegatee1, - toFixed(percentsToDelegate / 100), - { - from: delegator2, - } - ) - }) - - it('should delegate votes correctly', async () => { - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), - delegatedAmount * 2 - ) - await assertDelegatorDelegateeAmounts( - delegator, - delegatee1, - percentsToDelegate, - delegatedAmount, - lockedGold - ) - - await assertDelegatorDelegateeAmounts( - delegator2, - delegatee1, - percentsToDelegate, - delegatedAmount, - lockedGold - ) - }) - }) - }) - }) - }) - - describe('When vote signers', () => { - describe('When some gold is locked', () => { - const value = 1000 - let delegatorSigner - let delegatorSigner2 - let delegateeSigner1 - let delegateeSigner2 - - beforeEach(async () => { - await lockedGold.lock({ value: value.toString(), from: delegator }) - await lockedGold.lock({ value: value.toString(), from: delegator2 }) - ;[delegatorSigner, delegateeSigner1] = await createAndAssertDelegatorDelegateeSigners( - accountsInstance, - accounts, - delegator, - delegatee1 - ) - ;[delegatorSigner2, delegateeSigner2] = await createAndAssertDelegatorDelegateeSigners( - accountsInstance, - accounts, - delegator2, - delegatee2 - ) - }) - - describe('When delegatee account is registered', () => { - const percentsToDelegate = 10 - const delegatedAmount = (value / 100) * percentsToDelegate - beforeEach(async () => { - assertEqualBN(await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), 0) - assertEqualBN(await lockedGold.totalDelegatedCelo(delegatee1), 0) - assertEqualBN(await lockedGold.getAccountTotalGovernanceVotingPower(delegatee2), 0) - assertEqualBN(await lockedGold.totalDelegatedCelo(delegator), 0) - assertEqualBN(await lockedGold.getAccountTotalDelegatedFraction(delegator), 0) - }) - - it('should revert when incorrect percent amount is inserted', async () => { - await assertTransactionRevertWithReason( - lockedGold.delegateGovernanceVotes(ZERO_ADDRESS, toFixed(101 / 100)), - 'Delegate fraction must be less than or equal to 1' - ) - }) - - describe('When delegator is voting in referendum', () => { - beforeEach(async () => { - await mockGovernance.setTotalVotes(delegator, 1) - }) - - it('should revert when delegating votes that are currently voting for proposal', async () => { - await assertTransactionRevertWithReason( - lockedGold.delegateGovernanceVotes(delegateeSigner1, toFixed(100 / 100), { - from: delegatorSigner, - }), - 'Cannot delegate votes that are voting in referendum' - ) - }) - - it('should revert when voting for proposal with votes that are currently used in referendum (2 delegatees)', async () => { - await lockedGold.delegateGovernanceVotes(delegateeSigner1, toFixed(99 / 100), { - from: delegator, - }) - await assertTransactionRevertWithReason( - lockedGold.delegateGovernanceVotes(delegateeSigner2, toFixed(1 / 100), { - from: delegator, - }), - 'Cannot delegate votes that are voting in referendum' - ) - }) - - it('should delegate when voting for less than requested for delegation', async () => { - await lockedGold.delegateGovernanceVotes(delegateeSigner1, toFixed(99 / 100), { - from: delegatorSigner, - }) - }) - }) - - describe('When delegating to delegatee1', () => { - let resp: Truffle.TransactionResponse - beforeEach(async () => { - resp = await lockedGold.delegateGovernanceVotes( - delegateeSigner1, - toFixed(percentsToDelegate / 100), - { from: delegatorSigner } - ) - }) - it('should revert when delegating more than 100% in two steps (different delegatees)', async () => { - await assertTransactionRevertWithReason( - lockedGold.delegateGovernanceVotes(delegateeSigner2, toFixed(100 / 100), { - from: delegatorSigner, - }), - 'Cannot delegate more than 100%' - ) - }) - - it('should delegate correctly when delegated to same account in two steps', async () => { - assertEqualBN(await lockedGold.totalDelegatedCelo(delegatee1), delegatedAmount) - assertEqualBN(await lockedGold.totalDelegatedCelo(delegateeSigner1), 0) - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), - delegatedAmount - ) - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegateeSigner1), - 0 - ) - assertEqualBN( - fromFixed( - await lockedGold.getAccountTotalDelegatedFraction(delegator) - ).multipliedBy(100), - percentsToDelegate - ) - assertEqualBN( - fromFixed(await lockedGold.getAccountTotalDelegatedFraction(delegatorSigner)), - 0 - ) - await lockedGold.delegateGovernanceVotes(delegateeSigner1, toFixed(100 / 100), { - from: delegatorSigner, - }) - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), - value - ) - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegateeSigner1), - 0 - ) - await assertDelegatorDelegateeAmounts(delegator, delegatee1, 100, value, lockedGold) - await assertDelegatorDelegateeAmounts( - delegatorSigner, - delegateeSigner1, - 0, - 0, - lockedGold - ) - assertEqualBN( - fromFixed( - await lockedGold.getAccountTotalDelegatedFraction(delegator) - ).multipliedBy(100), - 100 - ) - assertEqualBN(await lockedGold.getAccountTotalDelegatedFraction(delegatorSigner), 0) - }) - - it('should emit the CeloDelegated event', async () => { - assert.equal(resp.logs.length, 1) - const log = resp.logs[0] - assertLogMatches2(log, { - event: 'CeloDelegated', - args: { - delegator, - delegatee: delegatee1, - percent: toFixed(percentsToDelegate / 100), - amount: delegatedAmount, - }, - }) - }) - - it('should delegate votes correctly', async () => { - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), - delegatedAmount - ) - await assertDelegatorDelegateeAmounts( - delegator, - delegatee1, - percentsToDelegate, - delegatedAmount, - lockedGold - ) - }) - - it('should delegate votes correctly to multiple accounts', async () => { - await lockedGold.delegateGovernanceVotes( - delegateeSigner2, - toFixed(percentsToDelegate / 100), - { - from: delegatorSigner, - } - ) - - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), - delegatedAmount - ) - - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegateeSigner1), - 0 - ) - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee2), - delegatedAmount - ) - - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegateeSigner2), - 0 - ) - - await assertDelegatorDelegateeAmounts( - delegator, - delegatee1, - percentsToDelegate, - delegatedAmount, - lockedGold - ) - await assertDelegatorDelegateeAmounts( - delegator, - delegatee2, - percentsToDelegate, - delegatedAmount, - lockedGold - ) - }) - - describe('When locked more gold and redelagate', () => { - let resp2: Truffle.TransactionResponse - beforeEach(async () => { - await lockedGold.lock({ value: value.toString(), from: delegator }) - resp2 = await lockedGold.delegateGovernanceVotes( - delegateeSigner1, - toFixed(percentsToDelegate / 100), - { from: delegatorSigner } - ) - }) - - it('should delegate votes correctly', async () => { - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), - delegatedAmount * 2 - ) - await assertDelegatorDelegateeAmounts( - delegator, - delegatee1, - percentsToDelegate, - delegatedAmount * 2, - lockedGold - ) - }) - - it('should emit the CeloDelegated event', async () => { - assert.equal(resp2.logs.length, 1) - const log = resp2.logs[0] - assertLogMatches2(log, { - event: 'CeloDelegated', - args: { - delegator, - delegatee: delegatee1, - percent: toFixed(percentsToDelegate / 100), - amount: delegatedAmount * 2, - }, - }) - }) - }) - }) - - describe('When 2 delegators are delegating to delegatee1', () => { - beforeEach(async () => { - await lockedGold.delegateGovernanceVotes( - delegateeSigner1, - toFixed(percentsToDelegate / 100), - { - from: delegatorSigner, - } - ) - await lockedGold.delegateGovernanceVotes( - delegateeSigner1, - toFixed(percentsToDelegate / 100), - { - from: delegatorSigner2, - } - ) - }) - - it('should delegate votes correctly', async () => { - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), - delegatedAmount * 2 - ) - await assertDelegatorDelegateeAmounts( - delegator, - delegatee1, - percentsToDelegate, - delegatedAmount, - lockedGold - ) - - await assertDelegatorDelegateeAmounts( - delegator2, - delegatee1, - percentsToDelegate, - delegatedAmount, - lockedGold - ) - }) - }) - }) - }) - }) - - describe('When trying to delegate to more then maxDelegateeCount', () => { - const value = 1000 - - beforeEach(async () => { - await lockedGold.setMaxDelegateesCount(2) - await lockedGold.lock({ value: value.toString(), from: delegator }) - }) - - describe('When delegated to allow count yet', () => { - beforeEach(async () => { - await lockedGold.delegateGovernanceVotes(delegatee1, toFixed(1 / 100), { - from: delegator, - }) - await lockedGold.delegateGovernanceVotes(delegatee2, toFixed(1 / 100), { - from: delegator, - }) - }) - - it('should return delegatees correctly', async () => { - const delegatees = await lockedGold.getDelegateesOfDelegator(delegator) - assert.sameDeepMembers([delegatee1, delegatee2], delegatees) - }) - - it('should revert when trying to add extra delegatee', async () => { - await assertTransactionRevertWithReason( - lockedGold.delegateGovernanceVotes(delegatee3, toFixed(1 / 100), { from: delegator }), - 'Too many delegatees' - ) - }) - - describe('When limit is increased', () => { - beforeEach(async () => { - await lockedGold.setMaxDelegateesCount(3) - }) - - it('should allow to increase number of validators', async () => { - await lockedGold.delegateGovernanceVotes(delegatee3, toFixed(1 / 100), { - from: delegator, - }) - }) - }) - }) - }) - - describe('When trying to delegate to more then maxDelegateeCount with vote signers', () => { - const value = 1000 - let delegatorSigner: string - let delegateeSigner1: string - let delegateeSigner2: string - let delegateeSigner3: string - - beforeEach(async () => { - await lockedGold.setMaxDelegateesCount(2) - await lockedGold.lock({ value: value.toString(), from: delegator }) - ;[delegatorSigner, delegateeSigner1] = await createAndAssertDelegatorDelegateeSigners( - accountsInstance, - accounts, - delegator, - delegatee1 - ) - ;[delegateeSigner3, delegateeSigner2] = await createAndAssertDelegatorDelegateeSigners( - accountsInstance, - accounts, - delegatee3, - delegatee2 - ) - }) - - describe('When delegated to allow count yet', () => { - beforeEach(async () => { - await lockedGold.delegateGovernanceVotes(delegateeSigner1, toFixed(1 / 100), { - from: delegatorSigner, - }) - await lockedGold.delegateGovernanceVotes(delegateeSigner2, toFixed(1 / 100), { - from: delegatorSigner, - }) - }) - - it('should return delegatees correctly', async () => { - const delegatees = await lockedGold.getDelegateesOfDelegator(delegator) - assert.sameDeepMembers([delegatee1, delegatee2], delegatees) - }) - - it('should revert when trying to add extra delegatee', async () => { - await assertTransactionRevertWithReason( - lockedGold.delegateGovernanceVotes(delegateeSigner3, toFixed(1 / 100), { - from: delegatorSigner, - }), - 'Too many delegatees' - ) - }) - - describe('When limit is increased', () => { - beforeEach(async () => { - await lockedGold.setMaxDelegateesCount(3) - }) - - it('should allow to increase number of validators', async () => { - await lockedGold.delegateGovernanceVotes(delegateeSigner3, toFixed(1 / 100), { - from: delegatorSigner, - }) - }) - }) - }) - }) - }) - }) - - describe('#revokeDelegatedGovernanceVotes()', () => { - it('should revert when incorrect percent amount is inserted', async () => { - await assertTransactionRevertWithReason( - lockedGold.revokeDelegatedGovernanceVotes(ZERO_ADDRESS, toFixed(101 / 100)), - 'Revoke fraction must be less than or equal to 1' - ) - }) - - it('should revert when nothing delegated', async () => { - await assertTransactionRevertWithReason( - lockedGold.revokeDelegatedGovernanceVotes(ZERO_ADDRESS, toFixed(10 / 100)), - 'Not enough total delegated percents' - ) - }) - - describe('When no vote signers', () => { - describe('When having delegated amount', () => { - const value = 1000 - const delegator = accounts[0] - const delegator2 = accounts[1] - const delegatee1 = accounts[5] - const delegatee2 = accounts[6] - - const percentsToDelegate = 10 - const delegatedAmount = (value / 100) * percentsToDelegate - - beforeEach(async () => { - await accountsInstance.createAccount({ from: delegator2 }) - await accountsInstance.createAccount({ from: delegatee1 }) - await accountsInstance.createAccount({ from: delegatee2 }) - - await lockedGold.lock({ value: value.toString(), from: delegator }) - await lockedGold.lock({ value: value.toString(), from: delegator2 }) - - await lockedGold.delegateGovernanceVotes(delegatee1, toFixed(percentsToDelegate / 100)) - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), - delegatedAmount - ) - assertEqualBN(await lockedGold.totalDelegatedCelo(delegatee1), delegatedAmount) - }) - - it('should revert when trying to revert more percent than delegated', async () => { - await assertTransactionRevertWithReason( - lockedGold.revokeDelegatedGovernanceVotes(ZERO_ADDRESS, toFixed(100 / 100)), - 'Not enough total delegated percents' - ) - }) - - describe('When revoking from delegatee1', () => { - const percentageToRevoke = 2 - const amountToRevoke = (value / 100) * percentageToRevoke - let resp: Truffle.TransactionResponse - - beforeEach(async () => { - resp = await lockedGold.revokeDelegatedGovernanceVotes( - delegatee1, - toFixed(percentageToRevoke / 100) - ) - }) - - it('should revoke votes correctly when delegatee not voting', async () => { - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), - delegatedAmount - amountToRevoke - ) - await assertDelegatorDelegateeAmounts( - delegator, - delegatee1, - percentsToDelegate - percentageToRevoke, - delegatedAmount - amountToRevoke, - lockedGold - ) - - assertEqualBN( - await lockedGold.totalDelegatedCelo(delegatee1), - delegatedAmount - amountToRevoke - ) - }) - - it('should emit the DelegatedCeloRevoked event', async () => { - assert.equal(resp.logs.length, 1) - const log = resp.logs[0] - assertLogMatches2(log, { - event: 'DelegatedCeloRevoked', - args: { - delegator, - delegatee: delegatee1, - percent: toFixed(percentageToRevoke / 100), - amount: amountToRevoke, - }, - }) - }) - - describe('When delegator1 locked more gold + delegator2 also delegated to delegatee1', () => { - beforeEach(async () => { - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), - delegatedAmount - amountToRevoke - ) - await lockedGold.delegateGovernanceVotes( - delegatee1, - toFixed(percentsToDelegate / 100), - { - from: delegator2, - } - ) - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), - delegatedAmount + (delegatedAmount - amountToRevoke) - ) - await lockedGold.lock({ value: value.toString(), from: delegator }) - }) - - describe('When revoking percentage such as that with newly locked amount it would decrease below zero', () => { - const percentageToRevokeAfterLock = 6 - const votingCeloPercent = 100 - const votingAmount = - ((delegatedAmount * 2 - amountToRevoke) / 100) * votingCeloPercent - - beforeEach(async () => { - assertEqualBN( - fromFixed( - await lockedGold.getAccountTotalDelegatedFraction(delegator) - ).multipliedBy(100), - percentsToDelegate - percentageToRevoke - ) - await mockGovernance.setTotalVotes(delegatee1, votingAmount) - - await lockedGold.revokeDelegatedGovernanceVotes( - delegatee1, - toFixed(percentageToRevokeAfterLock / 100) - ) - }) - - it('should revoke votes correctly when delegatee voting', async () => { - const amountFromDelegator1AfterRevoke = - ((2 * delegatedAmount) / percentsToDelegate) * - (percentsToDelegate - percentageToRevoke - percentageToRevokeAfterLock) - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), - delegatedAmount + amountFromDelegator1AfterRevoke // from delegator2 + from delegator1 after revoke - ) - await assertDelegatorDelegateeAmounts( - delegator, - delegatee1, - percentsToDelegate - percentageToRevoke - percentageToRevokeAfterLock, - amountFromDelegator1AfterRevoke, - lockedGold - ) - await assertDelegatorDelegateeAmounts( - delegator2, - delegatee1, - percentsToDelegate, - delegatedAmount, - lockedGold // from delegator2 - ) - assertEqualBN( - await lockedGold.totalDelegatedCelo(delegatee1), - delegatedAmount + amountFromDelegator1AfterRevoke - ) - - await assertEqualBN( - await mockGovernance.removeVotesCalledFor(delegatee1), - delegatedAmount + amountFromDelegator1AfterRevoke - ) - }) - }) - - describe('When revoking percentage such as that with newly locked amount it would not decrease below zero', () => { - const percentageToRevokeAfterLock = 2 - const amountRevokedAfterNewLockWitUpdate = - ((value * 2) / 100) * (percentageToRevoke + percentageToRevokeAfterLock) - const delegatedAmountAfterLock = ((value * 2) / 100) * percentsToDelegate - - const votingCeloPercent = 100 - const votingAmount = - ((delegatedAmount * 2 - amountToRevoke) / 100) * votingCeloPercent - - beforeEach(async () => { - assertEqualBN( - fromFixed( - await lockedGold.getAccountTotalDelegatedFraction(delegator) - ).multipliedBy(100), - percentsToDelegate - percentageToRevoke - ) - - await mockGovernance.setTotalVotes(delegatee1, votingAmount) - - await lockedGold.revokeDelegatedGovernanceVotes( - delegatee1, - toFixed(percentageToRevokeAfterLock / 100) - ) - }) - - it('should revoke votes correctly when delegatee voting', async () => { - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), - delegatedAmount + (delegatedAmountAfterLock - amountRevokedAfterNewLockWitUpdate) - ) - await assertDelegatorDelegateeAmounts( - delegator, - delegatee1, - percentsToDelegate - percentageToRevoke - percentageToRevokeAfterLock, - delegatedAmountAfterLock - amountRevokedAfterNewLockWitUpdate, - lockedGold - ) - await assertDelegatorDelegateeAmounts( - delegator2, - delegatee1, - percentsToDelegate, - delegatedAmount, - lockedGold - ) - assertEqualBN( - await lockedGold.totalDelegatedCelo(delegatee1), - delegatedAmount + (delegatedAmountAfterLock - amountRevokedAfterNewLockWitUpdate) - ) - await assertEqualBN(await mockGovernance.removeVotesCalledFor(delegatee1), 0) - }) - }) - }) - }) - - describe('When delegated to 2 accounts', () => { - const percentageToRevoke = 2 - const amountToRevoke = (value / 100) * percentageToRevoke - - beforeEach(async () => { - await lockedGold.delegateGovernanceVotes(delegatee2, toFixed(percentsToDelegate / 100)) - await lockedGold.revokeDelegatedGovernanceVotes( - delegatee1, - toFixed(percentageToRevoke / 100) - ) - }) - - it('should revoke votes correctly when delegatee not voting (delegated to 2 accounts)', async () => { - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), - delegatedAmount - amountToRevoke - ) - await assertDelegatorDelegateeAmounts( - delegator, - delegatee1, - percentsToDelegate - percentageToRevoke, - delegatedAmount - amountToRevoke, - lockedGold - ) - - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee2), - delegatedAmount - ) - await assertDelegatorDelegateeAmounts( - delegator, - delegatee2, - percentsToDelegate, - delegatedAmount, - lockedGold - ) - - assertEqualBN( - await lockedGold.totalDelegatedCelo(delegatee1), - delegatedAmount - amountToRevoke - ) - assertEqualBN(await lockedGold.totalDelegatedCelo(delegatee2), delegatedAmount) - }) - }) - - describe('When delegatee is voting', () => { - const votingWeight = 100 - beforeEach(async () => { - await mockGovernance.setTotalVotes(delegatee1, votingWeight) - }) - - it('should revoke votes correctly when delegatee is voting', async () => { - const percentageToRevoke = 9 - const amountToRevoke = (value / 100) * percentageToRevoke - await lockedGold.revokeDelegatedGovernanceVotes( - delegatee1, - toFixed(percentageToRevoke / 100) - ) - - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), - delegatedAmount - amountToRevoke - ) - const [fraction, currentAmount] = await lockedGold.getDelegatorDelegateeInfo( - delegator, - delegatee1 - ) - assertEqualBN( - fromFixed(fraction).multipliedBy(100), - percentsToDelegate - percentageToRevoke - ) - assertEqualBN(currentAmount, delegatedAmount - amountToRevoke) - - assertEqualBN( - await mockGovernance.removeVotesCalledFor(delegatee1), - delegatedAmount - amountToRevoke - ) - assertEqualBN( - await lockedGold.totalDelegatedCelo(delegatee1), - delegatedAmount - amountToRevoke - ) - }) - }) - }) - }) - - describe('When vote signers', () => { - describe('When having delegated amount', () => { - const value = 1000 - const delegator = accounts[0] - const delegator2 = accounts[1] - const delegatee1 = accounts[5] - const delegatee2 = accounts[6] - let delegatorSigner - let delegatee1Signer - let delegator2Signer - let delegatee2Signer - - const percentsToDelegate = 10 - const delegatedAmount = (value / 100) * percentsToDelegate - - beforeEach(async () => { - await accountsInstance.createAccount({ from: delegator2 }) - await accountsInstance.createAccount({ from: delegatee1 }) - await accountsInstance.createAccount({ from: delegatee2 }) - - await lockedGold.lock({ value: value.toString(), from: delegator }) - await lockedGold.lock({ value: value.toString(), from: delegator2 }) - ;[delegatorSigner, delegatee1Signer] = await createAndAssertDelegatorDelegateeSigners( - accountsInstance, - accounts, - delegator, - delegatee1 - ) - ;[delegator2Signer, delegatee2Signer] = await createAndAssertDelegatorDelegateeSigners( - accountsInstance, - accounts, - delegator2, - delegatee2 - ) - - await lockedGold.delegateGovernanceVotes( - delegatee1Signer, - toFixed(percentsToDelegate / 100), - { - from: delegatorSigner, - } - ) - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), - delegatedAmount - ) - assertEqualBN(await lockedGold.totalDelegatedCelo(delegatee1), delegatedAmount) - assertEqualBN(await lockedGold.totalDelegatedCelo(delegatee1Signer), 0) - }) - - it('should revert when trying to revert more percent than delegated', async () => { - await assertTransactionRevertWithReason( - lockedGold.revokeDelegatedGovernanceVotes(ZERO_ADDRESS, toFixed(100 / 100)), - 'Not enough total delegated percents' - ) - }) - - describe('When revoking from delegatee1', () => { - const percentageToRevoke = 2 - const amountToRevoke = (value / 100) * percentageToRevoke - let resp: Truffle.TransactionResponse - - beforeEach(async () => { - resp = await lockedGold.revokeDelegatedGovernanceVotes( - delegatee1Signer, - toFixed(percentageToRevoke / 100), - { from: delegatorSigner } - ) - }) - - it('should revoke votes correctly when delegatee not voting', async () => { - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), - delegatedAmount - amountToRevoke - ) - await assertDelegatorDelegateeAmounts( - delegator, - delegatee1, - percentsToDelegate - percentageToRevoke, - delegatedAmount - amountToRevoke, - lockedGold - ) - - assertEqualBN( - await lockedGold.totalDelegatedCelo(delegatee1), - delegatedAmount - amountToRevoke - ) - - assertEqualBN(await lockedGold.totalDelegatedCelo(delegatee1Signer), 0) - }) - - it('should emit the DelegatedCeloRevoked event', async () => { - assert.equal(resp.logs.length, 1) - const log = resp.logs[0] - assertLogMatches2(log, { - event: 'DelegatedCeloRevoked', - args: { - delegator, - delegatee: delegatee1, - percent: toFixed(percentageToRevoke / 100), - amount: amountToRevoke, - }, - }) - }) - - describe('When delegator1 locked more gold + delegator2 also delegated to delegatee1', () => { - beforeEach(async () => { - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), - delegatedAmount - amountToRevoke - ) - await lockedGold.delegateGovernanceVotes( - delegatee1Signer, - toFixed(percentsToDelegate / 100), - { - from: delegator2Signer, - } - ) - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), - delegatedAmount + (delegatedAmount - amountToRevoke) - ) - await lockedGold.lock({ value: value.toString(), from: delegator }) - }) - - describe('When revoking percentage such as that with newly locked amount it would decrease below zero', () => { - const percentageToRevokeAfterLock = 6 - const votingCeloPercent = 100 - const votingAmount = - ((delegatedAmount * 2 - amountToRevoke) / 100) * votingCeloPercent - - beforeEach(async () => { - assertEqualBN( - fromFixed( - await lockedGold.getAccountTotalDelegatedFraction(delegator) - ).multipliedBy(100), - percentsToDelegate - percentageToRevoke - ) - await mockGovernance.setTotalVotes(delegatee1, votingAmount) - - await lockedGold.revokeDelegatedGovernanceVotes( - delegatee1, - toFixed(percentageToRevokeAfterLock / 100) - ) - }) - - it('should revoke votes correctly when delegatee voting', async () => { - const amountFromDelegator1AfterRevoke = - ((2 * delegatedAmount) / percentsToDelegate) * - (percentsToDelegate - percentageToRevoke - percentageToRevokeAfterLock) - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), - delegatedAmount + amountFromDelegator1AfterRevoke // from delegator2 + from delegator1 after revoke - ) - await assertDelegatorDelegateeAmounts( - delegator, - delegatee1, - percentsToDelegate - percentageToRevoke - percentageToRevokeAfterLock, - amountFromDelegator1AfterRevoke, - lockedGold - ) - await assertDelegatorDelegateeAmounts( - delegator2, - delegatee1, - percentsToDelegate, - delegatedAmount, - lockedGold // from delegator2 - ) - assertEqualBN( - await lockedGold.totalDelegatedCelo(delegatee1), - delegatedAmount + amountFromDelegator1AfterRevoke - ) - - await assertEqualBN( - await mockGovernance.removeVotesCalledFor(delegatee1), - delegatedAmount + amountFromDelegator1AfterRevoke - ) - }) - }) - - describe('When revoking percentage such as that with newly locked amount it would not decrease below zero', () => { - const percentageToRevokeAfterLock = 2 - const amountRevokedAfterNewLockWitUpdate = - ((value * 2) / 100) * (percentageToRevoke + percentageToRevokeAfterLock) - const delegatedAmountAfterLock = ((value * 2) / 100) * percentsToDelegate - - const votingCeloPercent = 100 - const votingAmount = - ((delegatedAmount * 2 - amountToRevoke) / 100) * votingCeloPercent - - beforeEach(async () => { - assertEqualBN( - fromFixed( - await lockedGold.getAccountTotalDelegatedFraction(delegator) - ).multipliedBy(100), - percentsToDelegate - percentageToRevoke - ) - - await mockGovernance.setTotalVotes(delegatee1, votingAmount) - - await lockedGold.revokeDelegatedGovernanceVotes( - delegatee1, - toFixed(percentageToRevokeAfterLock / 100) - ) - }) - - it('should revoke votes correctly when delegatee voting', async () => { - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), - delegatedAmount + (delegatedAmountAfterLock - amountRevokedAfterNewLockWitUpdate) - ) - await assertDelegatorDelegateeAmounts( - delegator, - delegatee1, - percentsToDelegate - percentageToRevoke - percentageToRevokeAfterLock, - delegatedAmountAfterLock - amountRevokedAfterNewLockWitUpdate, - lockedGold - ) - await assertDelegatorDelegateeAmounts( - delegator2, - delegatee1, - percentsToDelegate, - delegatedAmount, - lockedGold - ) - assertEqualBN( - await lockedGold.totalDelegatedCelo(delegatee1), - delegatedAmount + (delegatedAmountAfterLock - amountRevokedAfterNewLockWitUpdate) - ) - await assertEqualBN(await mockGovernance.removeVotesCalledFor(delegatee1), 0) - }) - }) - }) - }) - - describe('When delegated to 2 accounts', () => { - const percentageToRevoke = 2 - const amountToRevoke = (value / 100) * percentageToRevoke - - beforeEach(async () => { - await lockedGold.delegateGovernanceVotes( - delegatee2Signer, - toFixed(percentsToDelegate / 100), - { - from: delegatorSigner, - } - ) - await lockedGold.revokeDelegatedGovernanceVotes( - delegatee1Signer, - toFixed(percentageToRevoke / 100), - { - from: delegatorSigner, - } - ) - }) - - it('should revoke votes correctly when delegatee not voting (delegated to 2 accounts)', async () => { - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), - delegatedAmount - amountToRevoke - ) - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1Signer), - 0 - ) - await assertDelegatorDelegateeAmounts( - delegator, - delegatee1, - percentsToDelegate - percentageToRevoke, - delegatedAmount - amountToRevoke, - lockedGold - ) - - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee2), - delegatedAmount - ) - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee2Signer), - 0 - ) - await assertDelegatorDelegateeAmounts( - delegator, - delegatee2, - percentsToDelegate, - delegatedAmount, - lockedGold - ) - - assertEqualBN( - await lockedGold.totalDelegatedCelo(delegatee1), - delegatedAmount - amountToRevoke - ) - assertEqualBN(await lockedGold.totalDelegatedCelo(delegatee2), delegatedAmount) - }) - }) - - describe('When delegatee is voting', () => { - const votingWeight = 100 - beforeEach(async () => { - await mockGovernance.setTotalVotes(delegatee1, votingWeight) - }) - - it('should revoke votes correctly when delegatee is voting', async () => { - const percentageToRevoke = 9 - const amountToRevoke = (value / 100) * percentageToRevoke - await lockedGold.revokeDelegatedGovernanceVotes( - delegatee1Signer, - toFixed(percentageToRevoke / 100), - { - from: delegatorSigner, - } - ) - - assertEqualBN( - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee1), - delegatedAmount - amountToRevoke - ) - const [fraction, currentAmount] = await lockedGold.getDelegatorDelegateeInfo( - delegator, - delegatee1 - ) - assertEqualBN( - fromFixed(fraction).multipliedBy(100), - percentsToDelegate - percentageToRevoke - ) - assertEqualBN(currentAmount, delegatedAmount - amountToRevoke) - - assertEqualBN( - await mockGovernance.removeVotesCalledFor(delegatee1), - delegatedAmount - amountToRevoke - ) - assertEqualBN( - await lockedGold.totalDelegatedCelo(delegatee1), - delegatedAmount - amountToRevoke - ) - }) - }) - }) - }) - }) - - describe('#getAccountTotalGovernanceVotingPower()', () => { - const delegatee = accounts[5] - const delegator = accounts[6] - const value = 1000 - it('should return 0 when nothing locked nor account', async () => { - const votingPower = await lockedGold.getAccountTotalGovernanceVotingPower(delegatee) - assertEqualBN(votingPower, 0) - }) - - describe('When having accounts', () => { - beforeEach(async () => { - await accountsInstance.createAccount({ from: delegatee }) - await accountsInstance.createAccount({ from: delegator }) - await lockedGold.lock({ value: value.toString(), from: delegator }) - }) - - describe('When only delegated', () => { - const delegatedPercent = 70 - const delegatedAmount = (value / 100) * delegatedPercent - - beforeEach(async () => { - await lockedGold.delegateGovernanceVotes(delegatee, toFixed(delegatedPercent / 100), { - from: delegator, - }) - }) - - it('should return correct value when locked and delegated (delegatee)', async () => { - const votingPower = await lockedGold.getAccountTotalGovernanceVotingPower(delegatee) - assertEqualBN(votingPower, delegatedAmount) - }) - - it('should return correct value when locked and delegated (delegator)', async () => { - const votingPower = await lockedGold.getAccountTotalGovernanceVotingPower(delegator) - assertEqualBN(votingPower, value - delegatedAmount) - }) - }) - - describe('When delegatee has locked celo', () => { - beforeEach(async () => { - await lockedGold.lock({ value: value.toString(), from: delegatee }) - }) - - it('should return correct value when locked', async () => { - const votingPower = await lockedGold.getAccountTotalGovernanceVotingPower(delegatee) - assertEqualBN(votingPower, value) - }) - - describe('When delegating', () => { - const delegatedPercent = 70 - const delegatedAmount = (value / 100) * delegatedPercent - - beforeEach(async () => { - await lockedGold.delegateGovernanceVotes(delegatee, toFixed(delegatedPercent / 100), { - from: delegator, - }) - }) - - it('should return correct value when locked and delegated (delegatee)', async () => { - const votingPower = await lockedGold.getAccountTotalGovernanceVotingPower(delegatee) - assertEqualBN(votingPower, value + delegatedAmount) - }) - - it('should return correct value when locked and delegated (delegator)', async () => { - const votingPower = await lockedGold.getAccountTotalGovernanceVotingPower(delegator) - assertEqualBN(votingPower, value - delegatedAmount) - }) - }) - }) - }) - }) - - describe('#getDelegatorDelegateeInfo()', () => { - const delegatee = accounts[5] - const delegator = accounts[6] - - it('should return 0 when nothing delegated', async () => { - const [fraction, delegatedAmount] = await lockedGold.getDelegatorDelegateeInfo( - delegatee, - delegator - ) - assertEqualBN(fromFixed(fraction).multipliedBy(100), 0) - assertEqualBN(delegatedAmount, 0) - }) - - describe('When locked celo', () => { - const value = 1000 - const delegatedPercent = 70 - const delegatedAmount = (value / 100) * delegatedPercent - - beforeEach(async () => { - await accountsInstance.createAccount({ from: delegatee }) - await accountsInstance.createAccount({ from: delegator }) - - await lockedGold.lock({ value: value.toString(), from: delegatee }) - await lockedGold.lock({ value: value.toString(), from: delegator }) - - await lockedGold.delegateGovernanceVotes(delegatee, toFixed(delegatedPercent / 100), { - from: delegator, - }) - }) - - it('should return correct percent and amount', async () => { - const [fraction, amount] = await lockedGold.getDelegatorDelegateeInfo(delegator, delegatee) - assertEqualBN(fromFixed(fraction).multipliedBy(100), delegatedPercent) - assertEqualBN(amount, delegatedAmount) - }) - }) - }) - - describe('#getDelegatorDelegateeExpectedAndRealAmount()', () => { - const delegatee = accounts[5] - const delegator = accounts[6] - - beforeEach(async () => { - await accountsInstance.createAccount({ from: delegatee }) - await accountsInstance.createAccount({ from: delegator }) - }) - - it('should return 0 when nothing delegated', async () => { - const [expected, actual] = await lockedGold.getDelegatorDelegateeExpectedAndRealAmount( - delegator, - delegatee - ) - assertEqualBN(expected, 0) - assertEqualBN(actual, 0) - }) - - describe('When delegated', () => { - const value = 1000 - const delegatedPercent = 70 - const delegatedAmount = (value / 100) * delegatedPercent - - beforeEach(async () => { - await lockedGold.lock({ value: value.toString(), from: delegator }) - await lockedGold.delegateGovernanceVotes(delegatee, toFixed(delegatedPercent / 100), { - from: delegator, - }) - }) - - it('should return equal amounts', async () => { - const [expected, actual] = await lockedGold.getDelegatorDelegateeExpectedAndRealAmount( - delegator, - delegatee - ) - assertEqualBN(expected, delegatedAmount) - assertEqualBN(actual, delegatedAmount) - }) - - describe('When locked more celo', () => { - const updatedDelegatedAmount = ((value * 2) / 100) * delegatedPercent - beforeEach(async () => { - await lockedGold.lock({ value: value.toString(), from: delegator }) - }) - - it('should return equal amounts', async () => { - const [expected, actual] = await lockedGold.getDelegatorDelegateeExpectedAndRealAmount( - delegator, - delegatee - ) - assertEqualBN(expected, updatedDelegatedAmount) - assertEqualBN(actual, updatedDelegatedAmount) - }) - - it('should update total voting power of delegatee', async () => { - const totalAccountGovernanceVotingPower = - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee) - assertEqualBN(totalAccountGovernanceVotingPower, delegatedAmount * 2) - }) - }) - }) - - describe('When having vote signer', () => { - let delegateeSigner - let delegatorSigner - - beforeEach(async () => { - ;[delegatorSigner, delegateeSigner] = await createAndAssertDelegatorDelegateeSigners( - accountsInstance, - accounts, - delegator, - delegatee - ) - }) - - describe('When delegated', () => { - const value = 1000 - const delegatedPercent = 70 - const delegatedAmount = (value / 100) * delegatedPercent - - beforeEach(async () => { - await lockedGold.lock({ value: value.toString(), from: delegator }) - await lockedGold.delegateGovernanceVotes( - delegateeSigner, - toFixed(delegatedPercent / 100), - { - from: delegatorSigner, - } - ) - }) - - it('should return equal amounts', async () => { - const [expectedSigner, actualSigner] = - await lockedGold.getDelegatorDelegateeExpectedAndRealAmount( - delegatorSigner, - delegateeSigner - ) - - const [expected, actual] = await lockedGold.getDelegatorDelegateeExpectedAndRealAmount( - delegator, - delegatee - ) - - assertEqualBN(expected, delegatedAmount) - assertEqualBN(actual, delegatedAmount) - assertEqualBN(expectedSigner, delegatedAmount) - assertEqualBN(actualSigner, delegatedAmount) - }) - - describe('When locked more celo', () => { - const updatedDelegatedAmount = ((value * 2) / 100) * delegatedPercent - beforeEach(async () => { - await lockedGold.lock({ value: value.toString(), from: delegator }) - }) - - it('should return equal amounts', async () => { - const [expectedSigner, actualSigner] = - await lockedGold.getDelegatorDelegateeExpectedAndRealAmount( - delegatorSigner, - delegateeSigner - ) - - const [expected, actual] = await lockedGold.getDelegatorDelegateeExpectedAndRealAmount( - delegator, - delegatee - ) - - assertEqualBN(expected, updatedDelegatedAmount) - assertEqualBN(actual, updatedDelegatedAmount) - assertEqualBN(expectedSigner, updatedDelegatedAmount) - assertEqualBN(actualSigner, updatedDelegatedAmount) - }) - - it('should update total voting power of delegatee', async () => { - const totalAccountGovernanceVotingPower = - await lockedGold.getAccountTotalGovernanceVotingPower(delegatee) - - const totalAccountGovernanceVotingPowerSigner = - await lockedGold.getAccountTotalGovernanceVotingPower(delegateeSigner) - - assertEqualBN(totalAccountGovernanceVotingPowerSigner, 0) - assertEqualBN(totalAccountGovernanceVotingPower, delegatedAmount * 2) - }) - }) - }) - }) - }) - - describe('#updateDelegatedAmount()', () => { - const delegatee = accounts[5] - const delegator = accounts[6] - - const value = 1000 - const delegatedPercent = 70 - const delegatedAmount = (value / 100) * delegatedPercent - - beforeEach(async () => { - await accountsInstance.createAccount({ from: delegatee }) - await accountsInstance.createAccount({ from: delegator }) - await lockedGold.lock({ value: value.toString(), from: delegator }) - }) - - describe('When no vote signer', () => { - beforeEach(async () => { - await lockedGold.delegateGovernanceVotes(delegatee, toFixed(delegatedPercent / 100), { - from: delegator, - }) - }) - - it('should return correct value when locked and delegated (delegatee)', async () => { - const votingPower = await lockedGold.getAccountTotalGovernanceVotingPower(delegatee) - assertEqualBN(votingPower, delegatedAmount) - await assertDelegatorDelegateeAmounts( - delegator, - delegatee, - delegatedPercent, - delegatedAmount, - lockedGold - ) - }) - - describe('When delegator locked more celo', () => { - beforeEach(async () => { - await lockedGold.lock({ value: value.toString(), from: delegator }) - await lockedGold.updateDelegatedAmount(delegator, delegatee) - }) - - it('should return correct value when locked and delegated (delegatee)', async () => { - const totalDelegatorLockedGold = await lockedGold.getAccountTotalLockedGold(delegator) - assertEqualBN(totalDelegatorLockedGold, value * 2) - - const votingPower = await lockedGold.getAccountTotalGovernanceVotingPower(delegatee) - assertEqualBN(votingPower, delegatedAmount * 2) - await assertDelegatorDelegateeAmounts( - delegator, - delegatee, - delegatedPercent, - delegatedAmount * 2, - lockedGold - ) - }) - }) - }) - - describe('When vote signer', () => { - let delegateeSigner - let delegatorSigner - - beforeEach(async () => { - ;[delegatorSigner, delegateeSigner] = await createAndAssertDelegatorDelegateeSigners( - accountsInstance, - accounts, - delegator, - delegatee - ) - await lockedGold.delegateGovernanceVotes(delegateeSigner, toFixed(delegatedPercent / 100), { - from: delegatorSigner, - }) - }) - - it('should return correct value when locked and delegated (delegatee)', async () => { - const votingPower = await lockedGold.getAccountTotalGovernanceVotingPower(delegatee) - assertEqualBN(votingPower, delegatedAmount) - await assertDelegatorDelegateeAmounts( - delegator, - delegatee, - delegatedPercent, - delegatedAmount, - lockedGold - ) - }) - - describe('When delegator locked more celo', () => { - beforeEach(async () => { - await lockedGold.lock({ value: value.toString(), from: delegator }) - await lockedGold.updateDelegatedAmount(delegatorSigner, delegateeSigner) - }) - - it('should return correct value when locked and delegated (delegatee)', async () => { - const totalDelegatorLockedGold = await lockedGold.getAccountTotalLockedGold(delegator) - assertEqualBN(totalDelegatorLockedGold, value * 2) - - const votingPower = await lockedGold.getAccountTotalGovernanceVotingPower(delegatee) - assertEqualBN(votingPower, delegatedAmount * 2) - await assertDelegatorDelegateeAmounts( - delegator, - delegatee, - delegatedPercent, - delegatedAmount * 2, - lockedGold - ) - }) - }) - }) - }) - - describe('#getTotalPendingWithdrawalsCount()', () => { - it('should return 0 if account has no pending withdrawals', async () => { - const count = await lockedGold.getTotalPendingWithdrawalsCount(account) - assert.equal(count.toNumber(), 0) - }) - - it('should return the count of pending withdrawals', async () => { - const value = 10000 - // @ts-ignore - await lockedGold.lock({ value }) - await lockedGold.unlock(value / 2) - await lockedGold.unlock(value / 2) - - const count = await lockedGold.getTotalPendingWithdrawalsCount(account) - assert.equal(count.toNumber(), 2) - }) - - it('should return 0 for a non-existent account', async () => { - const nonExistentAccount = '0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef' - const count = await lockedGold.getTotalPendingWithdrawalsCount(nonExistentAccount) - assert.equal(count.toNumber(), 0) - }) - }) -})