From 8b8ef36378505475e306f9076f2a522055c071f9 Mon Sep 17 00:00:00 2001 From: ujenjt Date: Thu, 25 Jan 2024 14:13:12 +0100 Subject: [PATCH 001/100] chore: add voting manager feature for holders --- apps/voting/contracts/Voting.sol | 118 ++++++++++++++++++ apps/voting/test/helpers/assertArrayAsSets.js | 34 +++++ apps/voting/test/voting.js | 116 ++++++++++++++++- 3 files changed, 267 insertions(+), 1 deletion(-) create mode 100644 apps/voting/test/helpers/assertArrayAsSets.js diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index bc92ca009..b568b4d46 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -23,6 +23,7 @@ contract Voting is IForwarder, AragonApp { bytes32 public constant UNSAFELY_MODIFY_VOTE_TIME_ROLE = keccak256("UNSAFELY_MODIFY_VOTE_TIME_ROLE"); uint64 public constant PCT_BASE = 10 ** 18; // 0% = 0; 1% = 10^16; 100% = 10^18 + uint64 public constant MAX_HOLDERS_PER_MANAGER = 1024; // some reasonable number to mitigate unbound loop issue string private constant ERROR_NO_VOTE = "VOTING_NO_VOTE"; string private constant ERROR_INIT_PCTS = "VOTING_INIT_PCTS"; @@ -37,6 +38,12 @@ contract Voting is IForwarder, AragonApp { string private constant ERROR_CHANGE_VOTE_TIME = "VOTING_VOTE_TIME_TOO_SMALL"; string private constant ERROR_CHANGE_OBJECTION_TIME = "VOTING_OBJ_TIME_TOO_BIG"; string private constant ERROR_INIT_OBJ_TIME_TOO_BIG = "VOTING_INIT_OBJ_TIME_TOO_BIG"; + string private constant ERROR_CAN_NOT_VOTE_FOR = "VOTING_CAN_NOT_VOTE_FOR"; + string private constant ERROR_ZERO_ADDRESS_PASSED = "VOTING_ZERO_ADDRESS_PASSED"; + string private constant ERROR_MANAGER_NOT_SET = "VOTING_MANAGER_NOT_SET"; + string private constant ERROR_SELF_MANAGER = "VOTING_SELF_MANAGER"; + string private constant ERROR_MANAGER_SAME_AS_PREV = "VOTING_MANAGER_SAME_AS_PREV"; + string private constant ERROR_MAX_MANAGED_VOTERS_REACHED = "VOTING_MAX_MANAGED_VOTERS_REACHED"; enum VoterState { Absent, Yea, Nay } @@ -55,6 +62,10 @@ contract Voting is IForwarder, AragonApp { mapping (address => VoterState) voters; } + struct ManagedAddressList { + address[] addresses; + } + MiniMeToken public token; uint64 public supportRequiredPct; uint64 public minAcceptQuorumPct; @@ -65,6 +76,11 @@ contract Voting is IForwarder, AragonApp { uint256 public votesLength; uint64 public objectionPhaseTime; + // manager -> [managed holder address] + mapping(address => ManagedAddressList) private managedHolders; + // holder -> manager + mapping(address => address) private managers; + event StartVote(uint256 indexed voteId, address indexed creator, string metadata); event CastVote(uint256 indexed voteId, address indexed voter, bool supports, uint256 stake); event CastObjection(uint256 indexed voteId, address indexed voter, uint256 stake); @@ -73,6 +89,7 @@ contract Voting is IForwarder, AragonApp { event ChangeMinQuorum(uint64 minAcceptQuorumPct); event ChangeVoteTime(uint64 voteTime); event ChangeObjectionPhaseTime(uint64 objectionPhaseTime); + event ManagerSet(address indexed holder, address indexed previousVotingManager, address indexed newVotingManager); modifier voteExists(uint256 _voteId) { require(_voteId < votesLength, ERROR_NO_VOTE); @@ -104,6 +121,79 @@ contract Voting is IForwarder, AragonApp { objectionPhaseTime = _objectionPhaseTime; } + /** + * TODO: Calculate gas spending and add hint for more efficient look up in the array if needed + */ + function setVotingManager(address _manager) public { + require(_manager != address(0), ERROR_ZERO_ADDRESS_PASSED); + + address msgSender = msg.sender; + require(_manager != msg.sender, ERROR_SELF_MANAGER); + + address prevManager = managers[msgSender]; + require(_manager != prevManager, ERROR_MANAGER_SAME_AS_PREV); + + if (prevManager != address(0)) { + _removeManagedAddressFor(prevManager, msgSender); + } + + _addManagedAddressFor(_manager, msgSender); + managers[msgSender] = _manager; + + emit ManagerSet(msgSender, prevManager, _manager); + } + + /** + * TODO: Calculate gas spending and add hint for more efficient look up in the array if needed + */ + function removeVotingManager() public { + address msgSender = msg.sender; + address prevManager = managers[msgSender]; + require(prevManager != address(0), ERROR_MANAGER_NOT_SET); + + _removeManagedAddressFor(prevManager, msgSender); + managers[msgSender] = address(0); + + emit ManagerSet(msgSender, prevManager, address(0)); + } + + function _addManagedAddressFor(address _manager, address _holder) public { + require(_manager != address(0), ERROR_ZERO_ADDRESS_PASSED); + require(_holder != address(0), ERROR_ZERO_ADDRESS_PASSED); + + uint256 length = managedHolders[_manager].addresses.length; + require(length < MAX_HOLDERS_PER_MANAGER, ERROR_MAX_MANAGED_VOTERS_REACHED); + + managedHolders[_manager].addresses.push(_holder); + } + + function _removeManagedAddressFor(address _manager, address _holder) public { + require(_manager != address(0), ERROR_ZERO_ADDRESS_PASSED); + require(_holder != address(0), ERROR_ZERO_ADDRESS_PASSED); + + uint256 length = managedHolders[_manager].addresses.length; + for (uint256 i = 0; i < length; i++) { + if (managedHolders[_manager].addresses[i] == _holder) { + managedHolders[_manager].addresses[i] = managedHolders[_manager].addresses[length - 1]; + delete managedHolders[_manager].addresses[length - 1]; + managedHolders[_manager].addresses.length--; + break; + } + } + } + + function getManagedVoters(address _manager) public view returns (address[] memory) { + require(_manager != address(0), ERROR_ZERO_ADDRESS_PASSED); + return managedHolders[_manager].addresses; + } + + function getVotingManager(address _holder) public view returns (address) { + require(_holder != address(0), ERROR_ZERO_ADDRESS_PASSED); + return managers[_holder]; + } + + // TODO: add getter allowing easily calculate compound total voting power for set of managed holders for particular voteId + /** * @notice Change required support to `@formatPct(_supportRequiredPct)`% * @param _supportRequiredPct New required support @@ -203,6 +293,28 @@ contract Voting is IForwarder, AragonApp { _vote(_voteId, _supports, msg.sender); } + function voteFor(uint256 _voteId, bool _supports, address _voteFor) external voteExists(_voteId) { + require(_canVoteFor(msg.sender, _voteFor), ERROR_CAN_NOT_VOTE_FOR); + require(_canVote(_voteId, _voteFor), ERROR_CAN_NOT_VOTE); + require(!_supports || _getVotePhase(votes[_voteId]) == VotePhase.Main, ERROR_CAN_NOT_VOTE); + _vote(_voteId, _supports, _voteFor); + } + + /** + * TODO: Update the voteForMultiple function to allow voting with the entire voting power + * of both self-managed address and managed addresses / write voteWithFullPower function + */ + function voteForMultiple(uint256 _voteId, bool _supports, address[] _voteForList) external voteExists(_voteId) { + require(!_supports || _getVotePhase(votes[_voteId]) == VotePhase.Main, ERROR_CAN_NOT_VOTE); + + for (uint i = 0; i < _voteForList.length; i++) { + address _voteFor = _voteForList[i]; + require(_canVoteFor(msg.sender, _voteFor), ERROR_CAN_NOT_VOTE_FOR); + require(_canVote(_voteId, _voteFor), ERROR_CAN_NOT_VOTE); + _vote(_voteId, _supports, _voteFor); + } + } + /** * @notice Execute vote #`_voteId` * @dev Initialization check is implicitly provided by `voteExists()` as new votes can only be @@ -461,6 +573,12 @@ contract Voting is IForwarder, AragonApp { return _isVoteOpen(vote_) && token.balanceOfAt(_voter, vote_.snapshotBlock) > 0; } + function _canVoteFor(address _manager, address _voter) internal view returns (bool) { + require(_manager != address(0), ERROR_ZERO_ADDRESS_PASSED); + require(_voter != address(0), ERROR_ZERO_ADDRESS_PASSED); + return managers[_voter] == _manager; + } + /** * @dev Internal function to get the current phase of the vote. It assumes the queried vote exists. * @return VotePhase.Main if one can vote 'yes' or 'no', VotePhase.Objection if one can vote only 'no' or VotePhase.Closed if no votes are accepted diff --git a/apps/voting/test/helpers/assertArrayAsSets.js b/apps/voting/test/helpers/assertArrayAsSets.js new file mode 100644 index 000000000..b837e1705 --- /dev/null +++ b/apps/voting/test/helpers/assertArrayAsSets.js @@ -0,0 +1,34 @@ +const { assert } = require('chai') +const { isAddress, isBN, toChecksumAddress } = require('web3-utils') + +function normalizeArg(arg) { + if (isBN(arg)) { + return arg.toString(); + } else if (isAddress(arg)) { + return toChecksumAddress(arg); + } else if (arg && arg.address) { + // Web3.js or Truffle contract instance + return toChecksumAddress(arg.address); + } + + return arg; +} +function assertArraysEqualAsSets(actual, expected, errorMsg) { + actual = actual.map(normalizeArg); + expected = expected.map(normalizeArg); + + const setActual = new Set(actual); + const setExpected = new Set(expected); + + assert.equal(setActual.size, setExpected.size, errorMsg || "Arrays do not have the same length."); + + setActual.forEach(item => { + assert.isTrue(setExpected.has(item), errorMsg || "Arrays do not match as sets."); + }); + + setExpected.forEach(item => { + assert.isTrue(setActual.has(item), errorMsg || "Arrays do not match as sets."); + }); +} + +module.exports = assertArraysEqualAsSets \ No newline at end of file diff --git a/apps/voting/test/voting.js b/apps/voting/test/voting.js index 1fcc4cf77..372c43bac 100644 --- a/apps/voting/test/voting.js +++ b/apps/voting/test/voting.js @@ -1,4 +1,5 @@ const ERRORS = require('./helpers/errors') +const assertArraysEqualAsSets = require('./helpers/assertArrayAsSets') const { assertBn, assertRevert, assertAmountOfEvents, assertEvent } = require('@aragon/contract-helpers-test/src/asserts') const { pct16, bn, bigExp, getEventArgument, ZERO_ADDRESS } = require('@aragon/contract-helpers-test') const { newDao, installNewApp, encodeCallScript, ANY_ENTITY, EMPTY_CALLS_SCRIPT } = require('@aragon/contract-helpers-test/src/aragon-os') @@ -17,7 +18,7 @@ const VOTER_STATE = ['ABSENT', 'YEA', 'NAY'].reduce((state, key, index) => { }, {}) -contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, nonHolder]) => { +contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, manager1, manager2, nonHolder]) => { let votingBase, voting, token, executionTarget, aclP let CREATE_VOTES_ROLE, MODIFY_SUPPORT_ROLE, MODIFY_QUORUM_ROLE, UNSAFELY_MODIFY_VOTE_TIME_ROLE @@ -412,6 +413,65 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, n }) }) }) + + context('voting for', () => { + let script, voteId, creator, metadata + + const neededSupport = pct16(50) + const minimumAcceptanceQuorum = pct16(20) + + beforeEach(async () => { + token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', decimals, 'n', true) // empty parameters minime + + await token.generateTokens(holder20, bigExp(20, decimals)) + await token.generateTokens(holder29, bigExp(29, decimals)) + await token.generateTokens(holder51, bigExp(51, decimals)) + await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, 0) + await voting.setVotingManager(manager1, {from: holder29}) + await voting.setVotingManager(manager1, {from: holder51}) + + executionTarget = await ExecutionTarget.new() + + const action = {to: executionTarget.address, calldata: executionTarget.contract.methods.execute().encodeABI()} + script = encodeCallScript([action, action]) + + const receipt = await voting.methods['newVote(bytes,string,bool,bool)'](script, 'metadata', false, false, {from: holder51}); + voteId = getEventArgument(receipt, 'StartVote', 'voteId') + creator = getEventArgument(receipt, 'StartVote', 'creator') + metadata = getEventArgument(receipt, 'StartVote', 'metadata') + }) + + + it('manager can vote for holder', async () => { + const tx = await voting.voteFor(voteId, false, holder29, {from: manager1}) + assertEvent(tx, 'CastVote', {expectedArgs: {voteId: voteId, voter: holder29, supports: false}}) + assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'CastObjection', {expectedAmount: 0}) + + const state = await voting.getVote(voteId) + const voterState = await voting.getVoterState(voteId, holder29) + + assertBn(state[7], bigExp(29, decimals), 'nay vote should have been counted') + assert.equal(voterState, VOTER_STATE.NAY, 'holder29 should have nay voter status') + }) + + it('manager can vote for both holders', async () => { + const tx = await voting.voteForMultiple(voteId, false, [holder29, holder51], {from: manager1}) + assertEvent(tx, 'CastVote', {expectedArgs: {voteId: voteId, voter: holder29, supports: false}}) + assertEvent(tx, 'CastVote', {index: 1, expectedArgs: {voteId: voteId, voter: holder51, supports: false}}) + assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 2}) + assertAmountOfEvents(tx, 'CastObjection', {expectedAmount: 0}) + + const state = await voting.getVote(voteId) + assertBn(state[7], bigExp(80, decimals), 'nay vote should have been counted') + + const voterState29 = await voting.getVoterState(voteId, holder29) + assert.equal(voterState29, VOTER_STATE.NAY, 'holder29 should have nay voter status') + + const voterState51 = await voting.getVoterState(voteId, holder51) + assert.equal(voterState51, VOTER_STATE.NAY, 'holder51 should have nay voter status') + }) + }) } context('wrong initializations', () => { @@ -797,4 +857,58 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, n assert.isTrue(voteState[1], 'vite should be executed') }) }) + + context('voting manager', () => { + const neededSupport = pct16(50) + const minimumAcceptanceQuorum = pct16(20) + const decimals = 18 + + beforeEach(async () => { + token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', decimals, 'n', true) // empty parameters minime + + await token.generateTokens(holder20, bigExp(20, decimals)) + await token.generateTokens(holder29, bigExp(29, decimals)) + await token.generateTokens(holder51, bigExp(51, decimals)) + await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, 0) + + executionTarget = await ExecutionTarget.new() + }) + + it('holder can set voting manager', async () => { + const tx = await voting.setVotingManager(manager1, {from: holder29}) + assertEvent(tx, 'ManagerSet', { + expectedArgs: {holder: holder29, previousVotingManager: ZERO_ADDRESS, newVotingManager: manager1} + }) + assertAmountOfEvents(tx, 'ManagerSet', {expectedAmount: 1}) + + const manager = await voting.getVotingManager(holder29) + assert.equal(manager, manager1, 'holder29 should have manager1 as a voting manager') + + const managedHolders = await voting.getManagedVoters(manager1) + assertArraysEqualAsSets(managedHolders, [holder29], 'manager1 should manage holder29') + }) + + it('holder can remove voting managers', async () => { + await voting.setVotingManager(manager1, {from: holder29}) + + const tx = await voting.removeVotingManager({from: holder29}) + assertEvent(tx, 'ManagerSet', { + expectedArgs: {holder: holder29, previousVotingManager: manager1, newVotingManager: ZERO_ADDRESS} + }) + assertAmountOfEvents(tx, 'ManagerSet', {expectedAmount: 1}) + }) + + it('manager can manage several holders', async () => { + await voting.setVotingManager(manager1, {from: holder29}) + + const tx = await voting.setVotingManager(manager1, {from: holder51}) + assertEvent(tx, 'ManagerSet', { + expectedArgs: {holder: holder51, previousVotingManager: ZERO_ADDRESS, newVotingManager: manager1} + }) + assertAmountOfEvents(tx, 'ManagerSet', {expectedAmount: 1}) + + const managedHolders = await voting.getManagedVoters(manager1) + assertArraysEqualAsSets(managedHolders, [holder29, holder51], 'manager1 should manage holder29 and holder51') + }) + }) }) From c1fa2f1141cc49d5638356345c54990b200402bb Mon Sep 17 00:00:00 2001 From: ujenjt Date: Thu, 25 Jan 2024 15:15:00 +0100 Subject: [PATCH 002/100] chore: Prohibit managers from overwriting holder's votes, retain override for holders --- apps/voting/contracts/Voting.sol | 27 +++++++++++++-------- apps/voting/test/helpers/errors.js | 11 ++++++++- apps/voting/test/voting.js | 38 ++++++++++++++++++++++++++---- 3 files changed, 61 insertions(+), 15 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index b568b4d46..627a729aa 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -44,8 +44,9 @@ contract Voting is IForwarder, AragonApp { string private constant ERROR_SELF_MANAGER = "VOTING_SELF_MANAGER"; string private constant ERROR_MANAGER_SAME_AS_PREV = "VOTING_MANAGER_SAME_AS_PREV"; string private constant ERROR_MAX_MANAGED_VOTERS_REACHED = "VOTING_MAX_MANAGED_VOTERS_REACHED"; + string private constant ERROR_MANAGER_CANNOT_OVERWRITE_VOTE = "VOTING_MGR_CANT_OVERWRITE"; - enum VoterState { Absent, Yea, Nay } + enum VoterState { Absent, Yea, Nay, ManagerYea, ManagerNay } enum VotePhase { Main, Objection, Closed } @@ -290,14 +291,14 @@ contract Voting is IForwarder, AragonApp { function vote(uint256 _voteId, bool _supports, bool _executesIfDecided_deprecated) external voteExists(_voteId) { require(_canVote(_voteId, msg.sender), ERROR_CAN_NOT_VOTE); require(!_supports || _getVotePhase(votes[_voteId]) == VotePhase.Main, ERROR_CAN_NOT_VOTE); - _vote(_voteId, _supports, msg.sender); + _vote(_voteId, _supports, msg.sender, false); } function voteFor(uint256 _voteId, bool _supports, address _voteFor) external voteExists(_voteId) { require(_canVoteFor(msg.sender, _voteFor), ERROR_CAN_NOT_VOTE_FOR); require(_canVote(_voteId, _voteFor), ERROR_CAN_NOT_VOTE); require(!_supports || _getVotePhase(votes[_voteId]) == VotePhase.Main, ERROR_CAN_NOT_VOTE); - _vote(_voteId, _supports, _voteFor); + _vote(_voteId, _supports, _voteFor, true); } /** @@ -311,7 +312,7 @@ contract Voting is IForwarder, AragonApp { address _voteFor = _voteForList[i]; require(_canVoteFor(msg.sender, _voteFor), ERROR_CAN_NOT_VOTE_FOR); require(_canVote(_voteId, _voteFor), ERROR_CAN_NOT_VOTE); - _vote(_voteId, _supports, _voteFor); + _vote(_voteId, _supports, _voteFor, true); } } @@ -475,7 +476,7 @@ contract Voting is IForwarder, AragonApp { emit StartVote(voteId, msg.sender, _metadata); if (_castVote && _canVote(voteId, msg.sender)) { - _vote(voteId, true, msg.sender); + _vote(voteId, true, msg.sender, false); } } @@ -483,28 +484,34 @@ contract Voting is IForwarder, AragonApp { * @dev Internal function to cast a vote or object to. @dev It assumes that voter can support or object to the vote */ - function _vote(uint256 _voteId, bool _supports, address _voter) internal { + function _vote(uint256 _voteId, bool _supports, address _voter, bool _isManager) internal { Vote storage vote_ = votes[_voteId]; // This could re-enter, though we can assume the governance token is not malicious uint256 voterStake = token.balanceOfAt(_voter, vote_.snapshotBlock); VoterState state = vote_.voters[_voter]; + // Voting manager can't overwrite holder vote + if (_isManager && (state == VoterState.Yea || state == VoterState.Nay)) { + revert(ERROR_MANAGER_CANNOT_OVERWRITE_VOTE); + } + // If voter had previously voted, decrease count - if (state == VoterState.Yea) { + if (state == VoterState.Yea || state == VoterState.ManagerYea) { vote_.yea = vote_.yea.sub(voterStake); - } else if (state == VoterState.Nay) { + } else if (state == VoterState.Nay || state == VoterState.ManagerNay) { vote_.nay = vote_.nay.sub(voterStake); } if (_supports) { vote_.yea = vote_.yea.add(voterStake); - vote_.voters[_voter] = VoterState.Yea; + vote_.voters[_voter] = _isManager ? VoterState.ManagerYea : VoterState.Yea; } else { vote_.nay = vote_.nay.add(voterStake); - vote_.voters[_voter] = VoterState.Nay; + vote_.voters[_voter] = _isManager ? VoterState.ManagerNay : VoterState.Nay; } + // TODO: consider to add an event indicates that manager have voted emit CastVote(_voteId, _voter, _supports, voterStake); if (_getVotePhase(vote_) == VotePhase.Objection) { diff --git a/apps/voting/test/helpers/errors.js b/apps/voting/test/helpers/errors.js index 872f01685..2b2ce1e4f 100644 --- a/apps/voting/test/helpers/errors.js +++ b/apps/voting/test/helpers/errors.js @@ -20,5 +20,14 @@ module.exports = makeErrorMappingProxy({ VOTING_NO_VOTING_POWER: 'VOTING_NO_VOTING_POWER', VOTING_OBJ_TIME_TOO_BIG: 'VOTING_OBJ_TIME_TOO_BIG', VOTING_VOTE_TIME_TOO_SMALL: 'VOTING_VOTE_TIME_TOO_SMALL', - VOTING_INIT_OBJ_TIME_TOO_BIG: 'VOTING_INIT_OBJ_TIME_TOO_BIG' + VOTING_INIT_OBJ_TIME_TOO_BIG: 'VOTING_INIT_OBJ_TIME_TOO_BIG', + + // Voting manager errors + VOTING_CAN_NOT_VOTE_FOR: 'VOTING_CAN_NOT_VOTE_FOR', + VOTING_ZERO_ADDRESS_PASSED: 'VOTING_ZERO_ADDRESS_PASSED', + VOTING_MANAGER_NOT_SET: 'VOTING_MANAGER_NOT_SET', + VOTING_SELF_MANAGER: 'VOTING_SELF_MANAGER', + VOTING_MANAGER_SAME_AS_PREV: 'VOTING_MANAGER_SAME_AS_PREV', + VOTING_MAX_MANAGED_VOTERS_REACHED: 'VOTING_MAX_MANAGED_VOTERS_REACHED', + VOTING_MGR_CANT_OVERWRITE: 'VOTING_MGR_CANT_OVERWRITE', }) diff --git a/apps/voting/test/voting.js b/apps/voting/test/voting.js index 372c43bac..39a2c4f6f 100644 --- a/apps/voting/test/voting.js +++ b/apps/voting/test/voting.js @@ -12,7 +12,7 @@ const ExecutionTarget = artifacts.require('ExecutionTarget') const createdVoteId = receipt => getEventArgument(receipt, 'StartVote', 'voteId') -const VOTER_STATE = ['ABSENT', 'YEA', 'NAY'].reduce((state, key, index) => { +const VOTER_STATE = ['ABSENT', 'YEA', 'NAY', 'MANAGER_YEA', 'MANAGER_NAY'].reduce((state, key, index) => { state[key] = index; return state; }, {}) @@ -452,7 +452,7 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, m const voterState = await voting.getVoterState(voteId, holder29) assertBn(state[7], bigExp(29, decimals), 'nay vote should have been counted') - assert.equal(voterState, VOTER_STATE.NAY, 'holder29 should have nay voter status') + assert.equal(voterState, VOTER_STATE.MANAGER_NAY, 'holder29 should have manager nay voter status') }) it('manager can vote for both holders', async () => { @@ -466,10 +466,40 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, m assertBn(state[7], bigExp(80, decimals), 'nay vote should have been counted') const voterState29 = await voting.getVoterState(voteId, holder29) - assert.equal(voterState29, VOTER_STATE.NAY, 'holder29 should have nay voter status') + assert.equal(voterState29, VOTER_STATE.MANAGER_NAY, 'holder29 should have manager nay voter status') const voterState51 = await voting.getVoterState(voteId, holder51) - assert.equal(voterState51, VOTER_STATE.NAY, 'holder51 should have nay voter status') + assert.equal(voterState51, VOTER_STATE.MANAGER_NAY, 'holder51 should have manager nay voter status') + }) + + it(`holder can overwrite manager's vote`, async () => { + await voting.voteFor(voteId, false, holder29, {from: manager1}) + + const tx = await voting.vote(voteId, true, true, {from: holder29}) + assertEvent(tx, 'CastVote', {expectedArgs: {voteId: voteId, voter: holder29, supports: true}}) + assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'CastObjection', {expectedAmount: 0}) + + const state = await voting.getVote(voteId) + assertBn(state[6], bigExp(29, decimals), 'yea vote should have been counted') + assertBn(state[7], bigExp(0, decimals), 'nay vote should have been reset') + + const voterState29 = await voting.getVoterState(voteId, holder29) + assert.equal(voterState29, VOTER_STATE.YEA, 'holder29 should have yea voter status') + }) + + it(`manager can't overwrite holder's vote`, async () => { + await voting.voteFor(voteId, false, holder29, {from: manager1}) + await voting.vote(voteId, true, true, {from: holder29}) + + await assertRevert( + voting.voteFor( + voteId, + false, + holder29, + { from: manager1 } + ), ERRORS.VOTING_MGR_CANT_OVERWRITE + ) }) }) } From 52a6524567aefa587194af161e8bd4473e593aeb Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Thu, 1 Feb 2024 15:49:31 +0100 Subject: [PATCH 003/100] fix: internal function types --- apps/voting/contracts/Voting.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 627a729aa..9c0cee147 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -158,7 +158,7 @@ contract Voting is IForwarder, AragonApp { emit ManagerSet(msgSender, prevManager, address(0)); } - function _addManagedAddressFor(address _manager, address _holder) public { + function _addManagedAddressFor(address _manager, address _holder) internal { require(_manager != address(0), ERROR_ZERO_ADDRESS_PASSED); require(_holder != address(0), ERROR_ZERO_ADDRESS_PASSED); @@ -168,7 +168,7 @@ contract Voting is IForwarder, AragonApp { managedHolders[_manager].addresses.push(_holder); } - function _removeManagedAddressFor(address _manager, address _holder) public { + function _removeManagedAddressFor(address _manager, address _holder) internal { require(_manager != address(0), ERROR_ZERO_ADDRESS_PASSED); require(_holder != address(0), ERROR_ZERO_ADDRESS_PASSED); From 7ed76a8de101826b28160c960b77cb0db3fe7525 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Thu, 1 Feb 2024 16:29:58 +0100 Subject: [PATCH 004/100] refactor: rename holder -> voter, manager -> delegate --- apps/voting/contracts/Voting.sol | 126 +++++++++++++++---------------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 9c0cee147..b344a0358 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -23,7 +23,7 @@ contract Voting is IForwarder, AragonApp { bytes32 public constant UNSAFELY_MODIFY_VOTE_TIME_ROLE = keccak256("UNSAFELY_MODIFY_VOTE_TIME_ROLE"); uint64 public constant PCT_BASE = 10 ** 18; // 0% = 0; 1% = 10^16; 100% = 10^18 - uint64 public constant MAX_HOLDERS_PER_MANAGER = 1024; // some reasonable number to mitigate unbound loop issue + uint64 public constant MAX_VOTERS_PER_DELEGATE = 1024; // some reasonable number to mitigate unbound loop issue string private constant ERROR_NO_VOTE = "VOTING_NO_VOTE"; string private constant ERROR_INIT_PCTS = "VOTING_INIT_PCTS"; @@ -40,13 +40,13 @@ contract Voting is IForwarder, AragonApp { string private constant ERROR_INIT_OBJ_TIME_TOO_BIG = "VOTING_INIT_OBJ_TIME_TOO_BIG"; string private constant ERROR_CAN_NOT_VOTE_FOR = "VOTING_CAN_NOT_VOTE_FOR"; string private constant ERROR_ZERO_ADDRESS_PASSED = "VOTING_ZERO_ADDRESS_PASSED"; - string private constant ERROR_MANAGER_NOT_SET = "VOTING_MANAGER_NOT_SET"; - string private constant ERROR_SELF_MANAGER = "VOTING_SELF_MANAGER"; - string private constant ERROR_MANAGER_SAME_AS_PREV = "VOTING_MANAGER_SAME_AS_PREV"; - string private constant ERROR_MAX_MANAGED_VOTERS_REACHED = "VOTING_MAX_MANAGED_VOTERS_REACHED"; - string private constant ERROR_MANAGER_CANNOT_OVERWRITE_VOTE = "VOTING_MGR_CANT_OVERWRITE"; + string private constant ERROR_DELEGATE_NOT_SET = "VOTING_DELEGATE_NOT_SET"; + string private constant ERROR_SELF_DELEGATE = "VOTING_SELF_DELEGATE"; + string private constant ERROR_DELEGATE_SAME_AS_PREV = "VOTING_DELEGATE_SAME_AS_PREV"; + string private constant ERROR_MAX_DELEGATED_VOTERS_REACHED = "VOTING_MAX_DELEGATED_VOTERS_REACHED"; + string private constant ERROR_DELEGATE_CANNOT_OVERWRITE_VOTE = "VOTING_MGR_CANT_OVERWRITE"; - enum VoterState { Absent, Yea, Nay, ManagerYea, ManagerNay } + enum VoterState { Absent, Yea, Nay, DelegateYea, DelegateNay } enum VotePhase { Main, Objection, Closed } @@ -63,7 +63,7 @@ contract Voting is IForwarder, AragonApp { mapping (address => VoterState) voters; } - struct ManagedAddressList { + struct DelegatedAddressList { address[] addresses; } @@ -77,10 +77,10 @@ contract Voting is IForwarder, AragonApp { uint256 public votesLength; uint64 public objectionPhaseTime; - // manager -> [managed holder address] - mapping(address => ManagedAddressList) private managedHolders; - // holder -> manager - mapping(address => address) private managers; + // delegate -> [delegated voter address] + mapping(address => DelegatedAddressList) private delegatedVoters; + // voter -> delegate + mapping(address => address) private delegates; event StartVote(uint256 indexed voteId, address indexed creator, string metadata); event CastVote(uint256 indexed voteId, address indexed voter, bool supports, uint256 stake); @@ -90,7 +90,7 @@ contract Voting is IForwarder, AragonApp { event ChangeMinQuorum(uint64 minAcceptQuorumPct); event ChangeVoteTime(uint64 voteTime); event ChangeObjectionPhaseTime(uint64 objectionPhaseTime); - event ManagerSet(address indexed holder, address indexed previousVotingManager, address indexed newVotingManager); + event DelegateSet(address indexed voter, address indexed previousDelegate, address indexed newDelegate); modifier voteExists(uint256 _voteId) { require(_voteId < votesLength, ERROR_NO_VOTE); @@ -125,75 +125,75 @@ contract Voting is IForwarder, AragonApp { /** * TODO: Calculate gas spending and add hint for more efficient look up in the array if needed */ - function setVotingManager(address _manager) public { - require(_manager != address(0), ERROR_ZERO_ADDRESS_PASSED); + function setDelegate(address _delegate) public { + require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); address msgSender = msg.sender; - require(_manager != msg.sender, ERROR_SELF_MANAGER); + require(_delegate != msgSender, ERROR_SELF_DELEGATE); - address prevManager = managers[msgSender]; - require(_manager != prevManager, ERROR_MANAGER_SAME_AS_PREV); + address prevDelegate = delegates[msgSender]; + require(_delegate != prevDelegate, ERROR_DELEGATE_SAME_AS_PREV); - if (prevManager != address(0)) { - _removeManagedAddressFor(prevManager, msgSender); + if (prevDelegate != address(0)) { + _removeDelegatedAddressFor(prevDelegate, msgSender); } - _addManagedAddressFor(_manager, msgSender); - managers[msgSender] = _manager; + _addDelegatedAddressFor(_delegate, msgSender); + delegates[msgSender] = _delegate; - emit ManagerSet(msgSender, prevManager, _manager); + emit DelegateSet(msgSender, prevDelegate, _delegate); } /** * TODO: Calculate gas spending and add hint for more efficient look up in the array if needed */ - function removeVotingManager() public { + function removeDelegate() public { address msgSender = msg.sender; - address prevManager = managers[msgSender]; - require(prevManager != address(0), ERROR_MANAGER_NOT_SET); + address prevDelegate = delegates[msgSender]; + require(prevDelegate != address(0), ERROR_DELEGATE_NOT_SET); - _removeManagedAddressFor(prevManager, msgSender); - managers[msgSender] = address(0); + _removeDelegatedAddressFor(prevDelegate, msgSender); + delegates[msgSender] = address(0); - emit ManagerSet(msgSender, prevManager, address(0)); + emit DelegateSet(msgSender, prevDelegate, address(0)); } - function _addManagedAddressFor(address _manager, address _holder) internal { - require(_manager != address(0), ERROR_ZERO_ADDRESS_PASSED); - require(_holder != address(0), ERROR_ZERO_ADDRESS_PASSED); + function _addDelegatedAddressFor(address _delegate, address _voter) internal { + require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); + require(_voter != address(0), ERROR_ZERO_ADDRESS_PASSED); - uint256 length = managedHolders[_manager].addresses.length; - require(length < MAX_HOLDERS_PER_MANAGER, ERROR_MAX_MANAGED_VOTERS_REACHED); + uint256 length = delegatedVoters[_delegate].addresses.length; + require(length < MAX_VOTERS_PER_DELEGATE, ERROR_MAX_DELEGATED_VOTERS_REACHED); - managedHolders[_manager].addresses.push(_holder); + delegatedVoters[_delegate].addresses.push(_voter); } - function _removeManagedAddressFor(address _manager, address _holder) internal { - require(_manager != address(0), ERROR_ZERO_ADDRESS_PASSED); - require(_holder != address(0), ERROR_ZERO_ADDRESS_PASSED); + function _removeDelegatedAddressFor(address _delegate, address _voter) internal { + require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); + require(_voter != address(0), ERROR_ZERO_ADDRESS_PASSED); - uint256 length = managedHolders[_manager].addresses.length; + uint256 length = delegatedVoters[_delegate].addresses.length; for (uint256 i = 0; i < length; i++) { - if (managedHolders[_manager].addresses[i] == _holder) { - managedHolders[_manager].addresses[i] = managedHolders[_manager].addresses[length - 1]; - delete managedHolders[_manager].addresses[length - 1]; - managedHolders[_manager].addresses.length--; + if (delegatedVoters[_delegate].addresses[i] == _voter) { + delegatedVoters[_delegate].addresses[i] = delegatedVoters[_delegate].addresses[length - 1]; + delete delegatedVoters[_delegate].addresses[length - 1]; + delegatedVoters[_delegate].addresses.length--; break; } } } - function getManagedVoters(address _manager) public view returns (address[] memory) { - require(_manager != address(0), ERROR_ZERO_ADDRESS_PASSED); - return managedHolders[_manager].addresses; + function getDelegatedVoters(address _delegate) public view returns (address[] memory) { + require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); + return delegatedVoters[_delegate].addresses; } - function getVotingManager(address _holder) public view returns (address) { - require(_holder != address(0), ERROR_ZERO_ADDRESS_PASSED); - return managers[_holder]; + function getDelegate(address _voter) public view returns (address) { + require(_voter != address(0), ERROR_ZERO_ADDRESS_PASSED); + return delegates[_voter]; } - // TODO: add getter allowing easily calculate compound total voting power for set of managed holders for particular voteId + // TODO: add getter allowing easily calculate compound total voting power for set of delegated voters for particular voteId /** * @notice Change required support to `@formatPct(_supportRequiredPct)`% @@ -303,7 +303,7 @@ contract Voting is IForwarder, AragonApp { /** * TODO: Update the voteForMultiple function to allow voting with the entire voting power - * of both self-managed address and managed addresses / write voteWithFullPower function + * of both self-delegated address and delegated addresses / write voteWithFullPower function */ function voteForMultiple(uint256 _voteId, bool _supports, address[] _voteForList) external voteExists(_voteId) { require(!_supports || _getVotePhase(votes[_voteId]) == VotePhase.Main, ERROR_CAN_NOT_VOTE); @@ -484,34 +484,34 @@ contract Voting is IForwarder, AragonApp { * @dev Internal function to cast a vote or object to. @dev It assumes that voter can support or object to the vote */ - function _vote(uint256 _voteId, bool _supports, address _voter, bool _isManager) internal { + function _vote(uint256 _voteId, bool _supports, address _voter, bool _isDelegate) internal { Vote storage vote_ = votes[_voteId]; // This could re-enter, though we can assume the governance token is not malicious uint256 voterStake = token.balanceOfAt(_voter, vote_.snapshotBlock); VoterState state = vote_.voters[_voter]; - // Voting manager can't overwrite holder vote - if (_isManager && (state == VoterState.Yea || state == VoterState.Nay)) { - revert(ERROR_MANAGER_CANNOT_OVERWRITE_VOTE); + // Delegate can't overwrite voter vote + if (_isDelegate && (state == VoterState.Yea || state == VoterState.Nay)) { + revert(ERROR_DELEGATE_CANNOT_OVERWRITE_VOTE); } // If voter had previously voted, decrease count - if (state == VoterState.Yea || state == VoterState.ManagerYea) { + if (state == VoterState.Yea || state == VoterState.DelegateYea) { vote_.yea = vote_.yea.sub(voterStake); - } else if (state == VoterState.Nay || state == VoterState.ManagerNay) { + } else if (state == VoterState.Nay || state == VoterState.DelegateNay) { vote_.nay = vote_.nay.sub(voterStake); } if (_supports) { vote_.yea = vote_.yea.add(voterStake); - vote_.voters[_voter] = _isManager ? VoterState.ManagerYea : VoterState.Yea; + vote_.voters[_voter] = _isDelegate ? VoterState.DelegateYea : VoterState.Yea; } else { vote_.nay = vote_.nay.add(voterStake); - vote_.voters[_voter] = _isManager ? VoterState.ManagerNay : VoterState.Nay; + vote_.voters[_voter] = _isDelegate ? VoterState.DelegateNay : VoterState.Nay; } - // TODO: consider to add an event indicates that manager have voted + // TODO: consider to add an event indicates that delegate have voted emit CastVote(_voteId, _voter, _supports, voterStake); if (_getVotePhase(vote_) == VotePhase.Objection) { @@ -580,10 +580,10 @@ contract Voting is IForwarder, AragonApp { return _isVoteOpen(vote_) && token.balanceOfAt(_voter, vote_.snapshotBlock) > 0; } - function _canVoteFor(address _manager, address _voter) internal view returns (bool) { - require(_manager != address(0), ERROR_ZERO_ADDRESS_PASSED); + function _canVoteFor(address _delegate, address _voter) internal view returns (bool) { + require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); require(_voter != address(0), ERROR_ZERO_ADDRESS_PASSED); - return managers[_voter] == _manager; + return delegates[_voter] == _delegate; } /** From 740bb2bd4b329afa492a55c018a1874d75197185 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Wed, 7 Feb 2024 16:21:52 +0100 Subject: [PATCH 005/100] refactor: update delegation related naming in tests --- apps/voting/contracts/Voting.sol | 2 +- apps/voting/test/helpers/errors.js | 10 ++-- apps/voting/test/voting.js | 85 +++++++++++++++--------------- 3 files changed, 48 insertions(+), 49 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index b344a0358..8ff6134c6 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -44,7 +44,7 @@ contract Voting is IForwarder, AragonApp { string private constant ERROR_SELF_DELEGATE = "VOTING_SELF_DELEGATE"; string private constant ERROR_DELEGATE_SAME_AS_PREV = "VOTING_DELEGATE_SAME_AS_PREV"; string private constant ERROR_MAX_DELEGATED_VOTERS_REACHED = "VOTING_MAX_DELEGATED_VOTERS_REACHED"; - string private constant ERROR_DELEGATE_CANNOT_OVERWRITE_VOTE = "VOTING_MGR_CANT_OVERWRITE"; + string private constant ERROR_DELEGATE_CANNOT_OVERWRITE_VOTE = "VOTING_DELEGATE_CANT_OVERWRITE"; enum VoterState { Absent, Yea, Nay, DelegateYea, DelegateNay } diff --git a/apps/voting/test/helpers/errors.js b/apps/voting/test/helpers/errors.js index 2b2ce1e4f..6e9e689d0 100644 --- a/apps/voting/test/helpers/errors.js +++ b/apps/voting/test/helpers/errors.js @@ -25,9 +25,9 @@ module.exports = makeErrorMappingProxy({ // Voting manager errors VOTING_CAN_NOT_VOTE_FOR: 'VOTING_CAN_NOT_VOTE_FOR', VOTING_ZERO_ADDRESS_PASSED: 'VOTING_ZERO_ADDRESS_PASSED', - VOTING_MANAGER_NOT_SET: 'VOTING_MANAGER_NOT_SET', - VOTING_SELF_MANAGER: 'VOTING_SELF_MANAGER', - VOTING_MANAGER_SAME_AS_PREV: 'VOTING_MANAGER_SAME_AS_PREV', - VOTING_MAX_MANAGED_VOTERS_REACHED: 'VOTING_MAX_MANAGED_VOTERS_REACHED', - VOTING_MGR_CANT_OVERWRITE: 'VOTING_MGR_CANT_OVERWRITE', + VOTING_DELEGATE_NOT_SET: 'VOTING_DELEGATE_NOT_SET', + VOTING_SELF_DELEGATE: 'VOTING_SELF_DELEGATE', + VOTING_DELEGATE_SAME_AS_PREV: 'VOTING_DELEGATE_SAME_AS_PREV', + VOTING_MAX_DELEGATED_VOTERS_REACHED: 'VOTING_MAX_DELEGATED_VOTERS_REACHED', + VOTING_DELEGATE_CANT_OVERWRITE: 'VOTING_DELEGATE_CANT_OVERWRITE', }) diff --git a/apps/voting/test/voting.js b/apps/voting/test/voting.js index 39a2c4f6f..cabcf3ab3 100644 --- a/apps/voting/test/voting.js +++ b/apps/voting/test/voting.js @@ -12,13 +12,12 @@ const ExecutionTarget = artifacts.require('ExecutionTarget') const createdVoteId = receipt => getEventArgument(receipt, 'StartVote', 'voteId') -const VOTER_STATE = ['ABSENT', 'YEA', 'NAY', 'MANAGER_YEA', 'MANAGER_NAY'].reduce((state, key, index) => { +const VOTER_STATE = ['ABSENT', 'YEA', 'NAY', 'DELEGATE_YEA', 'DELEGATE_NAY'].reduce((state, key, index) => { state[key] = index; return state; }, {}) - -contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, manager1, manager2, nonHolder]) => { +contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, delegate1, delegate2, nonHolder]) => { let votingBase, voting, token, executionTarget, aclP let CREATE_VOTES_ROLE, MODIFY_SUPPORT_ROLE, MODIFY_QUORUM_ROLE, UNSAFELY_MODIFY_VOTE_TIME_ROLE @@ -427,8 +426,8 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, m await token.generateTokens(holder29, bigExp(29, decimals)) await token.generateTokens(holder51, bigExp(51, decimals)) await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, 0) - await voting.setVotingManager(manager1, {from: holder29}) - await voting.setVotingManager(manager1, {from: holder51}) + await voting.setDelegate(delegate1, {from: holder29}) + await voting.setDelegate(delegate1, {from: holder51}) executionTarget = await ExecutionTarget.new() @@ -442,8 +441,8 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, m }) - it('manager can vote for holder', async () => { - const tx = await voting.voteFor(voteId, false, holder29, {from: manager1}) + it('delegate can vote for voter', async () => { + const tx = await voting.voteFor(voteId, false, holder29, {from: delegate1}) assertEvent(tx, 'CastVote', {expectedArgs: {voteId: voteId, voter: holder29, supports: false}}) assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) assertAmountOfEvents(tx, 'CastObjection', {expectedAmount: 0}) @@ -452,11 +451,11 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, m const voterState = await voting.getVoterState(voteId, holder29) assertBn(state[7], bigExp(29, decimals), 'nay vote should have been counted') - assert.equal(voterState, VOTER_STATE.MANAGER_NAY, 'holder29 should have manager nay voter status') + assert.equal(voterState, VOTER_STATE.DELEGATE_NAY, 'holder29 should have delegate nay voter status') }) - it('manager can vote for both holders', async () => { - const tx = await voting.voteForMultiple(voteId, false, [holder29, holder51], {from: manager1}) + it('delegate can vote for both voters', async () => { + const tx = await voting.voteForMultiple(voteId, false, [holder29, holder51], {from: delegate1}) assertEvent(tx, 'CastVote', {expectedArgs: {voteId: voteId, voter: holder29, supports: false}}) assertEvent(tx, 'CastVote', {index: 1, expectedArgs: {voteId: voteId, voter: holder51, supports: false}}) assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 2}) @@ -466,14 +465,14 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, m assertBn(state[7], bigExp(80, decimals), 'nay vote should have been counted') const voterState29 = await voting.getVoterState(voteId, holder29) - assert.equal(voterState29, VOTER_STATE.MANAGER_NAY, 'holder29 should have manager nay voter status') + assert.equal(voterState29, VOTER_STATE.DELEGATE_NAY, 'holder29 should have delegate nay voter status') const voterState51 = await voting.getVoterState(voteId, holder51) - assert.equal(voterState51, VOTER_STATE.MANAGER_NAY, 'holder51 should have manager nay voter status') + assert.equal(voterState51, VOTER_STATE.DELEGATE_NAY, 'holder51 should have delegate nay voter status') }) - it(`holder can overwrite manager's vote`, async () => { - await voting.voteFor(voteId, false, holder29, {from: manager1}) + it(`voter can overwrite delegate's vote`, async () => { + await voting.voteFor(voteId, false, holder29, {from: delegate1}) const tx = await voting.vote(voteId, true, true, {from: holder29}) assertEvent(tx, 'CastVote', {expectedArgs: {voteId: voteId, voter: holder29, supports: true}}) @@ -488,8 +487,8 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, m assert.equal(voterState29, VOTER_STATE.YEA, 'holder29 should have yea voter status') }) - it(`manager can't overwrite holder's vote`, async () => { - await voting.voteFor(voteId, false, holder29, {from: manager1}) + it(`delegate can't overwrite voter's vote`, async () => { + await voting.voteFor(voteId, false, holder29, {from: delegate1}) await voting.vote(voteId, true, true, {from: holder29}) await assertRevert( @@ -497,8 +496,8 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, m voteId, false, holder29, - { from: manager1 } - ), ERRORS.VOTING_MGR_CANT_OVERWRITE + { from: delegate1 } + ), ERRORS.VOTING_DELEGATE_CANT_OVERWRITE ) }) }) @@ -888,7 +887,7 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, m }) }) - context('voting manager', () => { + context('voting delegate', () => { const neededSupport = pct16(50) const minimumAcceptanceQuorum = pct16(20) const decimals = 18 @@ -904,41 +903,41 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, m executionTarget = await ExecutionTarget.new() }) - it('holder can set voting manager', async () => { - const tx = await voting.setVotingManager(manager1, {from: holder29}) - assertEvent(tx, 'ManagerSet', { - expectedArgs: {holder: holder29, previousVotingManager: ZERO_ADDRESS, newVotingManager: manager1} + it('voter can set delegate', async () => { + const tx = await voting.setDelegate(delegate1, {from: holder29}) + assertEvent(tx, 'DelegateSet', { + expectedArgs: {voter: holder29, previousDelegate: ZERO_ADDRESS, newDelegate: delegate1} }) - assertAmountOfEvents(tx, 'ManagerSet', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'DelegateSet', {expectedAmount: 1}) - const manager = await voting.getVotingManager(holder29) - assert.equal(manager, manager1, 'holder29 should have manager1 as a voting manager') + const delegate = await voting.getDelegate(holder29) + assert.equal(delegate, delegate1, 'holder29 should have delegate1 as a delegate') - const managedHolders = await voting.getManagedVoters(manager1) - assertArraysEqualAsSets(managedHolders, [holder29], 'manager1 should manage holder29') + const delegatedVoters = await voting.getDelegatedVoters(delegate1) + assertArraysEqualAsSets(delegatedVoters, [holder29], 'delegate1 should be a delegate of holder29') }) - it('holder can remove voting managers', async () => { - await voting.setVotingManager(manager1, {from: holder29}) + it('voter can remove delegates', async () => { + await voting.setDelegate(delegate1, {from: holder29}) - const tx = await voting.removeVotingManager({from: holder29}) - assertEvent(tx, 'ManagerSet', { - expectedArgs: {holder: holder29, previousVotingManager: manager1, newVotingManager: ZERO_ADDRESS} + const tx = await voting.removeDelegate({from: holder29}) + assertEvent(tx, 'DelegateSet', { + expectedArgs: {voter: holder29, previousDelegate: delegate1, newDelegate: ZERO_ADDRESS} }) - assertAmountOfEvents(tx, 'ManagerSet', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'DelegateSet', {expectedAmount: 1}) }) - it('manager can manage several holders', async () => { - await voting.setVotingManager(manager1, {from: holder29}) + it('delegate can manage several voters', async () => { + await voting.setDelegate(delegate1, {from: holder29}) - const tx = await voting.setVotingManager(manager1, {from: holder51}) - assertEvent(tx, 'ManagerSet', { - expectedArgs: {holder: holder51, previousVotingManager: ZERO_ADDRESS, newVotingManager: manager1} + const tx = await voting.setDelegate(delegate1, {from: holder51}) + assertEvent(tx, 'DelegateSet', { + expectedArgs: {voter: holder51, previousDelegate: ZERO_ADDRESS, newDelegate: delegate1} }) - assertAmountOfEvents(tx, 'ManagerSet', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'DelegateSet', {expectedAmount: 1}) - const managedHolders = await voting.getManagedVoters(manager1) - assertArraysEqualAsSets(managedHolders, [holder29, holder51], 'manager1 should manage holder29 and holder51') + const delegatedVoters = await voting.getDelegatedVoters(delegate1) + assertArraysEqualAsSets(delegatedVoters, [holder29, holder51], 'delegate1 should be a delegate of holder29 and holder51') }) }) -}) +}) \ No newline at end of file From 739ae3418a4ae2307efbd78db71e00b4ddf7504e Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Thu, 1 Feb 2024 18:04:48 +0100 Subject: [PATCH 006/100] refactor: extract vote phase check into separate fn --- apps/voting/contracts/Voting.sol | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 8ff6134c6..ea87f4513 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -290,14 +290,14 @@ contract Voting is IForwarder, AragonApp { */ function vote(uint256 _voteId, bool _supports, bool _executesIfDecided_deprecated) external voteExists(_voteId) { require(_canVote(_voteId, msg.sender), ERROR_CAN_NOT_VOTE); - require(!_supports || _getVotePhase(votes[_voteId]) == VotePhase.Main, ERROR_CAN_NOT_VOTE); + require(_canCastYeaVote(_voteId, _supports), ERROR_CAN_NOT_VOTE); _vote(_voteId, _supports, msg.sender, false); } function voteFor(uint256 _voteId, bool _supports, address _voteFor) external voteExists(_voteId) { require(_canVoteFor(msg.sender, _voteFor), ERROR_CAN_NOT_VOTE_FOR); require(_canVote(_voteId, _voteFor), ERROR_CAN_NOT_VOTE); - require(!_supports || _getVotePhase(votes[_voteId]) == VotePhase.Main, ERROR_CAN_NOT_VOTE); + require(_canCastYeaVote(_voteId, _supports), ERROR_CAN_NOT_VOTE); _vote(_voteId, _supports, _voteFor, true); } @@ -306,7 +306,7 @@ contract Voting is IForwarder, AragonApp { * of both self-delegated address and delegated addresses / write voteWithFullPower function */ function voteForMultiple(uint256 _voteId, bool _supports, address[] _voteForList) external voteExists(_voteId) { - require(!_supports || _getVotePhase(votes[_voteId]) == VotePhase.Main, ERROR_CAN_NOT_VOTE); + require(_canCastYeaVote(_voteId, _supports), ERROR_CAN_NOT_VOTE); for (uint i = 0; i < _voteForList.length; i++) { address _voteFor = _voteForList[i]; @@ -586,6 +586,10 @@ contract Voting is IForwarder, AragonApp { return delegates[_voter] == _delegate; } + function _canCastYeaVote(uint256 _voteId, bool _supports) internal view returns (bool) { + return !_supports || _getVotePhase(votes[_voteId]) == VotePhase.Main; + } + /** * @dev Internal function to get the current phase of the vote. It assumes the queried vote exists. * @return VotePhase.Main if one can vote 'yes' or 'no', VotePhase.Objection if one can vote only 'no' or VotePhase.Closed if no votes are accepted From 7392eb4691af47d1e3ece73ad60f751f6e8899d1 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Fri, 2 Feb 2024 14:36:00 +0100 Subject: [PATCH 007/100] refactor: add CastVoteAsDelegate event --- apps/voting/contracts/Voting.sol | 6 +++++- apps/voting/test/voting.js | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index ea87f4513..be6b5737a 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -91,6 +91,7 @@ contract Voting is IForwarder, AragonApp { event ChangeVoteTime(uint64 voteTime); event ChangeObjectionPhaseTime(uint64 objectionPhaseTime); event DelegateSet(address indexed voter, address indexed previousDelegate, address indexed newDelegate); + event CastVoteAsDelegate(uint256 indexed voteId, address indexed delegate, address indexed voter, bool supports, uint256 stake); modifier voteExists(uint256 _voteId) { require(_voteId < votesLength, ERROR_NO_VOTE); @@ -511,12 +512,15 @@ contract Voting is IForwarder, AragonApp { vote_.voters[_voter] = _isDelegate ? VoterState.DelegateNay : VoterState.Nay; } - // TODO: consider to add an event indicates that delegate have voted emit CastVote(_voteId, _voter, _supports, voterStake); if (_getVotePhase(vote_) == VotePhase.Objection) { emit CastObjection(_voteId, _voter, voterStake); } + + if (_isDelegate) { + emit CastVoteAsDelegate(_voteId, msg.sender, _voter, _supports, voterStake); + } } /** diff --git a/apps/voting/test/voting.js b/apps/voting/test/voting.js index cabcf3ab3..16e3a3720 100644 --- a/apps/voting/test/voting.js +++ b/apps/voting/test/voting.js @@ -444,8 +444,10 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d it('delegate can vote for voter', async () => { const tx = await voting.voteFor(voteId, false, holder29, {from: delegate1}) assertEvent(tx, 'CastVote', {expectedArgs: {voteId: voteId, voter: holder29, supports: false}}) + assertEvent(tx, 'CastVoteAsDelegate', {expectedArgs: {voteId: voteId, delegate: delegate1, voter: holder29, supports: false}}) assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) assertAmountOfEvents(tx, 'CastObjection', {expectedAmount: 0}) + assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 1}) const state = await voting.getVote(voteId) const voterState = await voting.getVoterState(voteId, holder29) @@ -458,8 +460,11 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d const tx = await voting.voteForMultiple(voteId, false, [holder29, holder51], {from: delegate1}) assertEvent(tx, 'CastVote', {expectedArgs: {voteId: voteId, voter: holder29, supports: false}}) assertEvent(tx, 'CastVote', {index: 1, expectedArgs: {voteId: voteId, voter: holder51, supports: false}}) + assertEvent(tx, 'CastVoteAsDelegate', {expectedArgs: {voteId: voteId, delegate: delegate1, voter: holder29, supports: false}}) + assertEvent(tx, 'CastVoteAsDelegate', {index: 1, expectedArgs: {voteId: voteId, delegate: delegate1, voter: holder51, supports: false}}) assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 2}) assertAmountOfEvents(tx, 'CastObjection', {expectedAmount: 0}) + assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 2}) const state = await voting.getVote(voteId) assertBn(state[7], bigExp(80, decimals), 'nay vote should have been counted') From 978b4eb426b4af995e707a3f326d96bf7957f6ed Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Wed, 7 Feb 2024 20:53:37 +0100 Subject: [PATCH 008/100] refactor: introduce Delegate struct and use it to simplify DelegatedAddressList management --- apps/voting/contracts/Voting.sol | 44 +++++++++++++++----------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index be6b5737a..d9159bc64 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -67,6 +67,11 @@ contract Voting is IForwarder, AragonApp { address[] addresses; } + struct Delegate { + address delegate; + uint256 voterIndex; + } + MiniMeToken public token; uint64 public supportRequiredPct; uint64 public minAcceptQuorumPct; @@ -80,7 +85,7 @@ contract Voting is IForwarder, AragonApp { // delegate -> [delegated voter address] mapping(address => DelegatedAddressList) private delegatedVoters; // voter -> delegate - mapping(address => address) private delegates; + mapping(address => Delegate) private delegates; event StartVote(uint256 indexed voteId, address indexed creator, string metadata); event CastVote(uint256 indexed voteId, address indexed voter, bool supports, uint256 stake); @@ -90,7 +95,7 @@ contract Voting is IForwarder, AragonApp { event ChangeMinQuorum(uint64 minAcceptQuorumPct); event ChangeVoteTime(uint64 voteTime); event ChangeObjectionPhaseTime(uint64 objectionPhaseTime); - event DelegateSet(address indexed voter, address indexed previousDelegate, address indexed newDelegate); + event DelegateSet(address indexed voter, address indexed previousDelegate, address indexed newDelegate, uint256 voterIndex); event CastVoteAsDelegate(uint256 indexed voteId, address indexed delegate, address indexed voter, bool supports, uint256 stake); modifier voteExists(uint256 _voteId) { @@ -132,7 +137,7 @@ contract Voting is IForwarder, AragonApp { address msgSender = msg.sender; require(_delegate != msgSender, ERROR_SELF_DELEGATE); - address prevDelegate = delegates[msgSender]; + address prevDelegate = delegates[msgSender].delegate; require(_delegate != prevDelegate, ERROR_DELEGATE_SAME_AS_PREV); if (prevDelegate != address(0)) { @@ -140,9 +145,10 @@ contract Voting is IForwarder, AragonApp { } _addDelegatedAddressFor(_delegate, msgSender); - delegates[msgSender] = _delegate; + uint256 voterIndex = delegatedVoters[_delegate].addresses.length - 1; + delegates[msgSender] = Delegate(_delegate, voterIndex); - emit DelegateSet(msgSender, prevDelegate, _delegate); + emit DelegateSet(msgSender, prevDelegate, _delegate, voterIndex); } /** @@ -150,38 +156,28 @@ contract Voting is IForwarder, AragonApp { */ function removeDelegate() public { address msgSender = msg.sender; - address prevDelegate = delegates[msgSender]; + address prevDelegate = delegates[msgSender].delegate; require(prevDelegate != address(0), ERROR_DELEGATE_NOT_SET); _removeDelegatedAddressFor(prevDelegate, msgSender); - delegates[msgSender] = address(0); + delegates[msgSender] = Delegate(address(0), 0); - emit DelegateSet(msgSender, prevDelegate, address(0)); + emit DelegateSet(msgSender, prevDelegate, address(0), 0); } function _addDelegatedAddressFor(address _delegate, address _voter) internal { - require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); - require(_voter != address(0), ERROR_ZERO_ADDRESS_PASSED); - uint256 length = delegatedVoters[_delegate].addresses.length; require(length < MAX_VOTERS_PER_DELEGATE, ERROR_MAX_DELEGATED_VOTERS_REACHED); delegatedVoters[_delegate].addresses.push(_voter); } - function _removeDelegatedAddressFor(address _delegate, address _voter) internal { - require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); - require(_voter != address(0), ERROR_ZERO_ADDRESS_PASSED); + uint256 voterIndex = delegates[_voter].voterIndex; uint256 length = delegatedVoters[_delegate].addresses.length; - for (uint256 i = 0; i < length; i++) { - if (delegatedVoters[_delegate].addresses[i] == _voter) { - delegatedVoters[_delegate].addresses[i] = delegatedVoters[_delegate].addresses[length - 1]; - delete delegatedVoters[_delegate].addresses[length - 1]; - delegatedVoters[_delegate].addresses.length--; - break; - } - } + delegatedVoters[_delegate].addresses[voterIndex] = delegatedVoters[_delegate].addresses[length - 1]; + delete delegatedVoters[_delegate].addresses[length - 1]; + delegatedVoters[_delegate].addresses.length--; } function getDelegatedVoters(address _delegate) public view returns (address[] memory) { @@ -191,7 +187,7 @@ contract Voting is IForwarder, AragonApp { function getDelegate(address _voter) public view returns (address) { require(_voter != address(0), ERROR_ZERO_ADDRESS_PASSED); - return delegates[_voter]; + return delegates[_voter].delegate; } // TODO: add getter allowing easily calculate compound total voting power for set of delegated voters for particular voteId @@ -587,7 +583,7 @@ contract Voting is IForwarder, AragonApp { function _canVoteFor(address _delegate, address _voter) internal view returns (bool) { require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); require(_voter != address(0), ERROR_ZERO_ADDRESS_PASSED); - return delegates[_voter] == _delegate; + return delegates[_voter].delegate == _delegate; } function _canCastYeaVote(uint256 _voteId, bool _supports) internal view returns (bool) { From e8c7965d7faf00e163ce904cdccf6ed767ce537a Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Wed, 7 Feb 2024 20:57:52 +0100 Subject: [PATCH 009/100] feat: add balance check to setDelegate --- apps/voting/contracts/Voting.sol | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index d9159bc64..72f673aab 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -140,6 +140,9 @@ contract Voting is IForwarder, AragonApp { address prevDelegate = delegates[msgSender].delegate; require(_delegate != prevDelegate, ERROR_DELEGATE_SAME_AS_PREV); + uint256 votingPower = token.balanceOfAt(msgSender, getBlockNumber64() - 1); + require(votingPower > 0, ERROR_NO_VOTING_POWER); + if (prevDelegate != address(0)) { _removeDelegatedAddressFor(prevDelegate, msgSender); } From baa1a71f75da545214e5a9a8c1f70bafca20811f Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Wed, 7 Feb 2024 21:18:38 +0100 Subject: [PATCH 010/100] refactor: remove reverts from voteForMultiple and _vote, move revert check from _vote to voteFor --- apps/voting/contracts/Voting.sol | 35 +++++++++++++++++++++--------- apps/voting/test/helpers/errors.js | 1 + apps/voting/test/voting.js | 25 +++++++++++++++++++++ 3 files changed, 51 insertions(+), 10 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 72f673aab..1d1087aa2 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -45,6 +45,7 @@ contract Voting is IForwarder, AragonApp { string private constant ERROR_DELEGATE_SAME_AS_PREV = "VOTING_DELEGATE_SAME_AS_PREV"; string private constant ERROR_MAX_DELEGATED_VOTERS_REACHED = "VOTING_MAX_DELEGATED_VOTERS_REACHED"; string private constant ERROR_DELEGATE_CANNOT_OVERWRITE_VOTE = "VOTING_DELEGATE_CANT_OVERWRITE"; + string private constant ERROR_CAN_NOT_VOTE_FOR_MULTIPLE = "VOTING_CAN_NOT_VOTE_FOR_MULTIPLE"; enum VoterState { Absent, Yea, Nay, DelegateYea, DelegateNay } @@ -97,6 +98,7 @@ contract Voting is IForwarder, AragonApp { event ChangeObjectionPhaseTime(uint64 objectionPhaseTime); event DelegateSet(address indexed voter, address indexed previousDelegate, address indexed newDelegate, uint256 voterIndex); event CastVoteAsDelegate(uint256 indexed voteId, address indexed delegate, address indexed voter, bool supports, uint256 stake); + event VoteForMultipleSkippedFor(uint256 indexed voteId, address indexed delegate, address indexed skippedVoter, bool supports); modifier voteExists(uint256 _voteId) { require(_voteId < votesLength, ERROR_NO_VOTE); @@ -298,6 +300,11 @@ contract Voting is IForwarder, AragonApp { require(_canVoteFor(msg.sender, _voteFor), ERROR_CAN_NOT_VOTE_FOR); require(_canVote(_voteId, _voteFor), ERROR_CAN_NOT_VOTE); require(_canCastYeaVote(_voteId, _supports), ERROR_CAN_NOT_VOTE); + + Vote storage vote_ = votes[_voteId]; + VoterState state = vote_.voters[_voteFor]; + require(state != VoterState.Yea && state != VoterState.Nay, ERROR_DELEGATE_CANNOT_OVERWRITE_VOTE); + _vote(_voteId, _supports, _voteFor, true); } @@ -306,14 +313,27 @@ contract Voting is IForwarder, AragonApp { * of both self-delegated address and delegated addresses / write voteWithFullPower function */ function voteForMultiple(uint256 _voteId, bool _supports, address[] _voteForList) external voteExists(_voteId) { + Vote storage vote_ = votes[_voteId]; + require(_isVoteOpen(vote_), ERROR_CAN_NOT_VOTE); require(_canCastYeaVote(_voteId, _supports), ERROR_CAN_NOT_VOTE); - for (uint i = 0; i < _voteForList.length; i++) { - address _voteFor = _voteForList[i]; - require(_canVoteFor(msg.sender, _voteFor), ERROR_CAN_NOT_VOTE_FOR); - require(_canVote(_voteId, _voteFor), ERROR_CAN_NOT_VOTE); - _vote(_voteId, _supports, _voteFor, true); + address msgSender = msg.sender; + uint256 length = _voteForList.length; + uint256 skippedVotersCount; + + for (uint256 i = 0; i < length; ++i) { + address voteFor_ = _voteForList[i]; + uint256 votingPower = token.balanceOfAt(voteFor_, vote_.snapshotBlock); + VoterState state = vote_.voters[voteFor_]; + if (_canVoteFor(msgSender, voteFor_) && votingPower > 0 && state != VoterState.Yea && state != VoterState.Nay) { + _vote(_voteId, _supports, voteFor_, true); + } else { + emit VoteForMultipleSkippedFor(_voteId, msgSender, voteFor_, _supports); + ++skippedVotersCount; + } } + + require(skippedVotersCount < length, ERROR_CAN_NOT_VOTE_FOR_MULTIPLE); } /** @@ -491,11 +511,6 @@ contract Voting is IForwarder, AragonApp { uint256 voterStake = token.balanceOfAt(_voter, vote_.snapshotBlock); VoterState state = vote_.voters[_voter]; - // Delegate can't overwrite voter vote - if (_isDelegate && (state == VoterState.Yea || state == VoterState.Nay)) { - revert(ERROR_DELEGATE_CANNOT_OVERWRITE_VOTE); - } - // If voter had previously voted, decrease count if (state == VoterState.Yea || state == VoterState.DelegateYea) { vote_.yea = vote_.yea.sub(voterStake); diff --git a/apps/voting/test/helpers/errors.js b/apps/voting/test/helpers/errors.js index 6e9e689d0..8cc694fb5 100644 --- a/apps/voting/test/helpers/errors.js +++ b/apps/voting/test/helpers/errors.js @@ -30,4 +30,5 @@ module.exports = makeErrorMappingProxy({ VOTING_DELEGATE_SAME_AS_PREV: 'VOTING_DELEGATE_SAME_AS_PREV', VOTING_MAX_DELEGATED_VOTERS_REACHED: 'VOTING_MAX_DELEGATED_VOTERS_REACHED', VOTING_DELEGATE_CANT_OVERWRITE: 'VOTING_DELEGATE_CANT_OVERWRITE', + VOTING_CAN_NOT_VOTE_FOR_MULTIPLE: 'VOTING_CAN_NOT_VOTE_FOR_MULTIPLE', }) diff --git a/apps/voting/test/voting.js b/apps/voting/test/voting.js index 16e3a3720..ee1e2f4d4 100644 --- a/apps/voting/test/voting.js +++ b/apps/voting/test/voting.js @@ -476,6 +476,31 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d assert.equal(voterState51, VOTER_STATE.DELEGATE_NAY, 'holder51 should have delegate nay voter status') }) + it(`delegate can vote for multiple even if some voters aren't valid`, async () => { + const tx = await voting.voteForMultiple(voteId, false, [holder29, holder51, holder1], {from: delegate1}) + assertEvent(tx, 'CastVote', {expectedArgs: {voteId: voteId, voter: holder29, supports: false}}) + assertEvent(tx, 'CastVote', {index: 1, expectedArgs: {voteId: voteId, voter: holder51, supports: false}}) + assertEvent(tx, 'CastVoteAsDelegate', {expectedArgs: {voteId: voteId, delegate: delegate1, voter: holder29, supports: false}}) + assertEvent(tx, 'CastVoteAsDelegate', {index: 1, expectedArgs: {voteId: voteId, delegate: delegate1, voter: holder51, supports: false}}) + assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 2}) + assertAmountOfEvents(tx, 'CastObjection', {expectedAmount: 0}) + assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 2}) + + assertEvent(tx, 'VoteForMultipleSkippedFor', {expectedArgs: {voteId: voteId, delegate: delegate1, supports: false, skippedVoter: holder1}}) + assertAmountOfEvents(tx, 'VoteForMultipleSkippedFor', {expectedAmount: 1}) + }) + + it(`revert if all voters aren't valid`, async () => { + await assertRevert( + voting.voteForMultiple( + voteId, + false, + [holder1, holder2], + {from: delegate1} + ), ERRORS.VOTING_CAN_NOT_VOTE_FOR_MULTIPLE + ) + }) + it(`voter can overwrite delegate's vote`, async () => { await voting.voteFor(voteId, false, holder29, {from: delegate1}) From 1d98abfc1665a5bc5c6050e229fe600d9950486b Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Thu, 8 Feb 2024 11:07:57 +0100 Subject: [PATCH 011/100] fix: update voterIndex of swapped address during voter deletion --- apps/voting/contracts/Voting.sol | 9 +++++---- apps/voting/test/voting.js | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 1d1087aa2..206d476e2 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -70,7 +70,7 @@ contract Voting is IForwarder, AragonApp { struct Delegate { address delegate; - uint256 voterIndex; + uint96 voterIndex; } MiniMeToken public token; @@ -96,7 +96,7 @@ contract Voting is IForwarder, AragonApp { event ChangeMinQuorum(uint64 minAcceptQuorumPct); event ChangeVoteTime(uint64 voteTime); event ChangeObjectionPhaseTime(uint64 objectionPhaseTime); - event DelegateSet(address indexed voter, address indexed previousDelegate, address indexed newDelegate, uint256 voterIndex); + event DelegateSet(address indexed voter, address indexed previousDelegate, address indexed newDelegate, uint96 voterIndex); event CastVoteAsDelegate(uint256 indexed voteId, address indexed delegate, address indexed voter, bool supports, uint256 stake); event VoteForMultipleSkippedFor(uint256 indexed voteId, address indexed delegate, address indexed skippedVoter, bool supports); @@ -150,7 +150,7 @@ contract Voting is IForwarder, AragonApp { } _addDelegatedAddressFor(_delegate, msgSender); - uint256 voterIndex = delegatedVoters[_delegate].addresses.length - 1; + uint96 voterIndex = uint96(delegatedVoters[_delegate].addresses.length - 1); delegates[msgSender] = Delegate(_delegate, voterIndex); emit DelegateSet(msgSender, prevDelegate, _delegate, voterIndex); @@ -177,10 +177,11 @@ contract Voting is IForwarder, AragonApp { delegatedVoters[_delegate].addresses.push(_voter); } function _removeDelegatedAddressFor(address _delegate, address _voter) internal { - uint256 voterIndex = delegates[_voter].voterIndex; + uint96 voterIndex = delegates[_voter].voterIndex; uint256 length = delegatedVoters[_delegate].addresses.length; delegatedVoters[_delegate].addresses[voterIndex] = delegatedVoters[_delegate].addresses[length - 1]; + delegates[delegatedVoters[_delegate].addresses[length - 1]].voterIndex = voterIndex; delete delegatedVoters[_delegate].addresses[length - 1]; delegatedVoters[_delegate].addresses.length--; } diff --git a/apps/voting/test/voting.js b/apps/voting/test/voting.js index ee1e2f4d4..6612470ee 100644 --- a/apps/voting/test/voting.js +++ b/apps/voting/test/voting.js @@ -957,6 +957,24 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d assertAmountOfEvents(tx, 'DelegateSet', {expectedAmount: 1}) }) + it('voters can remove delegates', async () => { + await voting.setDelegate(delegate1, {from: holder20}) + await voting.setDelegate(delegate1, {from: holder29}) + await voting.setDelegate(delegate1, {from: holder51}) + + + const tx1 = await voting.removeDelegate({from: holder29}) + assertEvent(tx1, 'DelegateSet', { + expectedArgs: {voter: holder29, previousDelegate: delegate1, newDelegate: ZERO_ADDRESS} + }) + assertAmountOfEvents(tx1, 'DelegateSet', {expectedAmount: 1}) + const tx2 = await voting.removeDelegate({from: holder51}) + assertEvent(tx2, 'DelegateSet', { + expectedArgs: {voter: holder51, previousDelegate: delegate1, newDelegate: ZERO_ADDRESS} + }) + assertAmountOfEvents(tx2, 'DelegateSet', {expectedAmount: 1}) + }) + it('delegate can manage several voters', async () => { await voting.setDelegate(delegate1, {from: holder29}) From 81a75b79c562070413fb07f726b93c9ed798a138 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Thu, 8 Feb 2024 11:13:17 +0100 Subject: [PATCH 012/100] refactor: remove voterIndex from DelegateSet event --- apps/voting/contracts/Voting.sol | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 206d476e2..8a066fabe 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -96,7 +96,7 @@ contract Voting is IForwarder, AragonApp { event ChangeMinQuorum(uint64 minAcceptQuorumPct); event ChangeVoteTime(uint64 voteTime); event ChangeObjectionPhaseTime(uint64 objectionPhaseTime); - event DelegateSet(address indexed voter, address indexed previousDelegate, address indexed newDelegate, uint96 voterIndex); + event DelegateSet(address indexed voter, address indexed previousDelegate, address indexed newDelegate); event CastVoteAsDelegate(uint256 indexed voteId, address indexed delegate, address indexed voter, bool supports, uint256 stake); event VoteForMultipleSkippedFor(uint256 indexed voteId, address indexed delegate, address indexed skippedVoter, bool supports); @@ -130,9 +130,6 @@ contract Voting is IForwarder, AragonApp { objectionPhaseTime = _objectionPhaseTime; } - /** - * TODO: Calculate gas spending and add hint for more efficient look up in the array if needed - */ function setDelegate(address _delegate) public { require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); @@ -150,15 +147,11 @@ contract Voting is IForwarder, AragonApp { } _addDelegatedAddressFor(_delegate, msgSender); - uint96 voterIndex = uint96(delegatedVoters[_delegate].addresses.length - 1); - delegates[msgSender] = Delegate(_delegate, voterIndex); + delegates[msgSender] = Delegate(_delegate, uint96(delegatedVoters[_delegate].addresses.length - 1)); - emit DelegateSet(msgSender, prevDelegate, _delegate, voterIndex); + emit DelegateSet(msgSender, prevDelegate, _delegate); } - /** - * TODO: Calculate gas spending and add hint for more efficient look up in the array if needed - */ function removeDelegate() public { address msgSender = msg.sender; address prevDelegate = delegates[msgSender].delegate; @@ -167,7 +160,7 @@ contract Voting is IForwarder, AragonApp { _removeDelegatedAddressFor(prevDelegate, msgSender); delegates[msgSender] = Delegate(address(0), 0); - emit DelegateSet(msgSender, prevDelegate, address(0), 0); + emit DelegateSet(msgSender, prevDelegate, address(0)); } function _addDelegatedAddressFor(address _delegate, address _voter) internal { @@ -309,10 +302,6 @@ contract Voting is IForwarder, AragonApp { _vote(_voteId, _supports, _voteFor, true); } - /** - * TODO: Update the voteForMultiple function to allow voting with the entire voting power - * of both self-delegated address and delegated addresses / write voteWithFullPower function - */ function voteForMultiple(uint256 _voteId, bool _supports, address[] _voteForList) external voteExists(_voteId) { Vote storage vote_ = votes[_voteId]; require(_isVoteOpen(vote_), ERROR_CAN_NOT_VOTE); From b0406191596cb2ea441051d03807b1cf672afc1e Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Thu, 8 Feb 2024 11:16:48 +0100 Subject: [PATCH 013/100] refactor: remove MAX_VOTERS_PER_DELEGATE limitation --- apps/voting/contracts/Voting.sol | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 8a066fabe..01ee4f16a 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -23,7 +23,6 @@ contract Voting is IForwarder, AragonApp { bytes32 public constant UNSAFELY_MODIFY_VOTE_TIME_ROLE = keccak256("UNSAFELY_MODIFY_VOTE_TIME_ROLE"); uint64 public constant PCT_BASE = 10 ** 18; // 0% = 0; 1% = 10^16; 100% = 10^18 - uint64 public constant MAX_VOTERS_PER_DELEGATE = 1024; // some reasonable number to mitigate unbound loop issue string private constant ERROR_NO_VOTE = "VOTING_NO_VOTE"; string private constant ERROR_INIT_PCTS = "VOTING_INIT_PCTS"; @@ -164,11 +163,9 @@ contract Voting is IForwarder, AragonApp { } function _addDelegatedAddressFor(address _delegate, address _voter) internal { - uint256 length = delegatedVoters[_delegate].addresses.length; - require(length < MAX_VOTERS_PER_DELEGATE, ERROR_MAX_DELEGATED_VOTERS_REACHED); - delegatedVoters[_delegate].addresses.push(_voter); } + function _removeDelegatedAddressFor(address _delegate, address _voter) internal { uint96 voterIndex = delegates[_voter].voterIndex; From 337b031e11ad56f5a228d1fe94e9012d195d69f4 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Thu, 8 Feb 2024 11:42:42 +0100 Subject: [PATCH 014/100] refactor: add offset and count params to getDelegatedVoters --- apps/voting/contracts/Voting.sol | 20 +++++++++++++++----- apps/voting/test/voting.js | 4 ++-- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 01ee4f16a..61ade5286 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -45,6 +45,7 @@ contract Voting is IForwarder, AragonApp { string private constant ERROR_MAX_DELEGATED_VOTERS_REACHED = "VOTING_MAX_DELEGATED_VOTERS_REACHED"; string private constant ERROR_DELEGATE_CANNOT_OVERWRITE_VOTE = "VOTING_DELEGATE_CANT_OVERWRITE"; string private constant ERROR_CAN_NOT_VOTE_FOR_MULTIPLE = "VOTING_CAN_NOT_VOTE_FOR_MULTIPLE"; + string private constant ERROR_INVALID_OFFSET_AND_COUNT = "VOTING_INVALID_OFFSET_AND_COUNT"; enum VoterState { Absent, Yea, Nay, DelegateYea, DelegateNay } @@ -165,7 +166,7 @@ contract Voting is IForwarder, AragonApp { function _addDelegatedAddressFor(address _delegate, address _voter) internal { delegatedVoters[_delegate].addresses.push(_voter); } - + function _removeDelegatedAddressFor(address _delegate, address _voter) internal { uint96 voterIndex = delegates[_voter].voterIndex; @@ -176,9 +177,20 @@ contract Voting is IForwarder, AragonApp { delegatedVoters[_delegate].addresses.length--; } - function getDelegatedVoters(address _delegate) public view returns (address[] memory) { + function getDelegatedVoters(address _delegate, uint96 offset, uint96 count) public view returns (address[] memory) { require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); - return delegatedVoters[_delegate].addresses; + require(count > 0 && offset >= 0 && offset + count <= delegatedVoters[_delegate].addresses.length, ERROR_INVALID_OFFSET_AND_COUNT); + + address[] memory result = new address[](count); + for (uint256 i = 0; i < count; ++i) { + result[i] = delegatedVoters[_delegate].addresses[offset + i]; + } + return result; + } + + function getTotalDelegatedVotersCount(address _delegate) public view returns (uint256) { + require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); + return delegatedVoters[_delegate].addresses.length; } function getDelegate(address _voter) public view returns (address) { @@ -186,8 +198,6 @@ contract Voting is IForwarder, AragonApp { return delegates[_voter].delegate; } - // TODO: add getter allowing easily calculate compound total voting power for set of delegated voters for particular voteId - /** * @notice Change required support to `@formatPct(_supportRequiredPct)`% * @param _supportRequiredPct New required support diff --git a/apps/voting/test/voting.js b/apps/voting/test/voting.js index 6612470ee..33e174006 100644 --- a/apps/voting/test/voting.js +++ b/apps/voting/test/voting.js @@ -943,7 +943,7 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d const delegate = await voting.getDelegate(holder29) assert.equal(delegate, delegate1, 'holder29 should have delegate1 as a delegate') - const delegatedVoters = await voting.getDelegatedVoters(delegate1) + const delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 1) assertArraysEqualAsSets(delegatedVoters, [holder29], 'delegate1 should be a delegate of holder29') }) @@ -984,7 +984,7 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d }) assertAmountOfEvents(tx, 'DelegateSet', {expectedAmount: 1}) - const delegatedVoters = await voting.getDelegatedVoters(delegate1) + const delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 2) assertArraysEqualAsSets(delegatedVoters, [holder29, holder51], 'delegate1 should be a delegate of holder29 and holder51') }) }) From f98f68d2f3dfbc20b7c07c004612561b9a0ea862 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Thu, 8 Feb 2024 12:19:28 +0100 Subject: [PATCH 015/100] refactor: remove unused constant --- apps/voting/contracts/Voting.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 61ade5286..dd6ca49e1 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -42,7 +42,6 @@ contract Voting is IForwarder, AragonApp { string private constant ERROR_DELEGATE_NOT_SET = "VOTING_DELEGATE_NOT_SET"; string private constant ERROR_SELF_DELEGATE = "VOTING_SELF_DELEGATE"; string private constant ERROR_DELEGATE_SAME_AS_PREV = "VOTING_DELEGATE_SAME_AS_PREV"; - string private constant ERROR_MAX_DELEGATED_VOTERS_REACHED = "VOTING_MAX_DELEGATED_VOTERS_REACHED"; string private constant ERROR_DELEGATE_CANNOT_OVERWRITE_VOTE = "VOTING_DELEGATE_CANT_OVERWRITE"; string private constant ERROR_CAN_NOT_VOTE_FOR_MULTIPLE = "VOTING_CAN_NOT_VOTE_FOR_MULTIPLE"; string private constant ERROR_INVALID_OFFSET_AND_COUNT = "VOTING_INVALID_OFFSET_AND_COUNT"; From 4d6f1508ddde39e22fc62794a472e8a64da7d691 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 13 Feb 2024 12:37:08 +0100 Subject: [PATCH 016/100] refactor: add revert if error inside loop in voteForMultiple --- apps/voting/contracts/Voting.sol | 50 +++++++++++++++----------------- apps/voting/test/voting.js | 27 ++++------------- 2 files changed, 29 insertions(+), 48 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index dd6ca49e1..19767d313 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -292,45 +292,34 @@ contract Voting is IForwarder, AragonApp { */ function vote(uint256 _voteId, bool _supports, bool _executesIfDecided_deprecated) external voteExists(_voteId) { require(_canVote(_voteId, msg.sender), ERROR_CAN_NOT_VOTE); - require(_canCastYeaVote(_voteId, _supports), ERROR_CAN_NOT_VOTE); + require(_isProperPhaseToVote(_voteId, _supports), ERROR_CAN_NOT_VOTE); _vote(_voteId, _supports, msg.sender, false); } function voteFor(uint256 _voteId, bool _supports, address _voteFor) external voteExists(_voteId) { - require(_canVoteFor(msg.sender, _voteFor), ERROR_CAN_NOT_VOTE_FOR); - require(_canVote(_voteId, _voteFor), ERROR_CAN_NOT_VOTE); - require(_canCastYeaVote(_voteId, _supports), ERROR_CAN_NOT_VOTE); - Vote storage vote_ = votes[_voteId]; - VoterState state = vote_.voters[_voteFor]; - require(state != VoterState.Yea && state != VoterState.Nay, ERROR_DELEGATE_CANNOT_OVERWRITE_VOTE); - + require(_isVoteOpen(vote_), ERROR_CAN_NOT_VOTE); + require(_isProperPhaseToVote(_voteId, _supports), ERROR_CAN_NOT_VOTE); + require(_hasEnoughVotingPower(vote_, _voteFor), ERROR_CAN_NOT_VOTE); + require(_canVoteFor(msg.sender, _voteFor), ERROR_CAN_NOT_VOTE_FOR); + require(!_hasVoted(_voteId, _voteFor), ERROR_DELEGATE_CANNOT_OVERWRITE_VOTE); _vote(_voteId, _supports, _voteFor, true); } function voteForMultiple(uint256 _voteId, bool _supports, address[] _voteForList) external voteExists(_voteId) { Vote storage vote_ = votes[_voteId]; require(_isVoteOpen(vote_), ERROR_CAN_NOT_VOTE); - require(_canCastYeaVote(_voteId, _supports), ERROR_CAN_NOT_VOTE); - - address msgSender = msg.sender; - uint256 length = _voteForList.length; - uint256 skippedVotersCount; + require(_isProperPhaseToVote(_voteId, _supports), ERROR_CAN_NOT_VOTE); - for (uint256 i = 0; i < length; ++i) { + for (uint256 i = 0; i < _voteForList.length; ++i) { address voteFor_ = _voteForList[i]; - uint256 votingPower = token.balanceOfAt(voteFor_, vote_.snapshotBlock); - VoterState state = vote_.voters[voteFor_]; - if (_canVoteFor(msgSender, voteFor_) && votingPower > 0 && state != VoterState.Yea && state != VoterState.Nay) { - _vote(_voteId, _supports, voteFor_, true); - } else { - emit VoteForMultipleSkippedFor(_voteId, msgSender, voteFor_, _supports); - ++skippedVotersCount; - } + require(_hasEnoughVotingPower(vote_, voteFor_), ERROR_CAN_NOT_VOTE); + require(_canVoteFor(msg.sender, voteFor_), ERROR_CAN_NOT_VOTE_FOR); + require(!_hasVoted(_voteId, voteFor_), ERROR_DELEGATE_CANNOT_OVERWRITE_VOTE); + _vote(_voteId, _supports, voteFor_, true); } - - require(skippedVotersCount < length, ERROR_CAN_NOT_VOTE_FOR_MULTIPLE); } + /** * @notice Execute vote #`_voteId` @@ -591,7 +580,7 @@ contract Voting is IForwarder, AragonApp { */ function _canVote(uint256 _voteId, address _voter) internal view returns (bool) { Vote storage vote_ = votes[_voteId]; - return _isVoteOpen(vote_) && token.balanceOfAt(_voter, vote_.snapshotBlock) > 0; + return _isVoteOpen(vote_) && _hasEnoughVotingPower(vote_, _voter); } function _canVoteFor(address _delegate, address _voter) internal view returns (bool) { @@ -600,10 +589,19 @@ contract Voting is IForwarder, AragonApp { return delegates[_voter].delegate == _delegate; } - function _canCastYeaVote(uint256 _voteId, bool _supports) internal view returns (bool) { + function _isProperPhaseToVote(uint256 _voteId, bool _supports) internal view returns (bool) { return !_supports || _getVotePhase(votes[_voteId]) == VotePhase.Main; } + function _hasVoted(uint256 _voteId, address _voter) internal view returns (bool) { + VoterState state = votes[_voteId].voters[_voter]; + return state == VoterState.Yea || state == VoterState.Nay; + } + + function _hasEnoughVotingPower(Vote storage vote_, address _voter) internal view returns (bool) { + return token.balanceOfAt(_voter, vote_.snapshotBlock) > 0; + } + /** * @dev Internal function to get the current phase of the vote. It assumes the queried vote exists. * @return VotePhase.Main if one can vote 'yes' or 'no', VotePhase.Objection if one can vote only 'no' or VotePhase.Closed if no votes are accepted diff --git a/apps/voting/test/voting.js b/apps/voting/test/voting.js index 33e174006..4d4dfc130 100644 --- a/apps/voting/test/voting.js +++ b/apps/voting/test/voting.js @@ -476,28 +476,11 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d assert.equal(voterState51, VOTER_STATE.DELEGATE_NAY, 'holder51 should have delegate nay voter status') }) - it(`delegate can vote for multiple even if some voters aren't valid`, async () => { - const tx = await voting.voteForMultiple(voteId, false, [holder29, holder51, holder1], {from: delegate1}) - assertEvent(tx, 'CastVote', {expectedArgs: {voteId: voteId, voter: holder29, supports: false}}) - assertEvent(tx, 'CastVote', {index: 1, expectedArgs: {voteId: voteId, voter: holder51, supports: false}}) - assertEvent(tx, 'CastVoteAsDelegate', {expectedArgs: {voteId: voteId, delegate: delegate1, voter: holder29, supports: false}}) - assertEvent(tx, 'CastVoteAsDelegate', {index: 1, expectedArgs: {voteId: voteId, delegate: delegate1, voter: holder51, supports: false}}) - assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 2}) - assertAmountOfEvents(tx, 'CastObjection', {expectedAmount: 0}) - assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 2}) - - assertEvent(tx, 'VoteForMultipleSkippedFor', {expectedArgs: {voteId: voteId, delegate: delegate1, supports: false, skippedVoter: holder1}}) - assertAmountOfEvents(tx, 'VoteForMultipleSkippedFor', {expectedAmount: 1}) - }) - - it(`revert if all voters aren't valid`, async () => { + it(`delegate can't vote for both voters if one has previously voted`, async () => { + await voting.vote(voteId, false, true, { from: holder29 }) await assertRevert( - voting.voteForMultiple( - voteId, - false, - [holder1, holder2], - {from: delegate1} - ), ERRORS.VOTING_CAN_NOT_VOTE_FOR_MULTIPLE + voting.voteForMultiple(voteId, false, [holder29, holder51], {from: delegate1}), + ERRORS.VOTING_DELEGATE_CANT_OVERWRITE ) }) @@ -988,4 +971,4 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d assertArraysEqualAsSets(delegatedVoters, [holder29, holder51], 'delegate1 should be a delegate of holder29 and holder51') }) }) -}) \ No newline at end of file +}) From 7bf40142649914f43e77ee123b9d9855939625a3 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 13 Feb 2024 12:52:41 +0100 Subject: [PATCH 017/100] refactor: add getter for delegated voters with their voting power and voting state --- apps/voting/contracts/Voting.sol | 43 +++++++++++++++++++++++++------- apps/voting/test/voting.js | 4 +-- 2 files changed, 36 insertions(+), 11 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 19767d313..25fcc4d8c 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -44,7 +44,7 @@ contract Voting is IForwarder, AragonApp { string private constant ERROR_DELEGATE_SAME_AS_PREV = "VOTING_DELEGATE_SAME_AS_PREV"; string private constant ERROR_DELEGATE_CANNOT_OVERWRITE_VOTE = "VOTING_DELEGATE_CANT_OVERWRITE"; string private constant ERROR_CAN_NOT_VOTE_FOR_MULTIPLE = "VOTING_CAN_NOT_VOTE_FOR_MULTIPLE"; - string private constant ERROR_INVALID_OFFSET_AND_COUNT = "VOTING_INVALID_OFFSET_AND_COUNT"; + string private constant ERROR_INVALID_OFFSET_OR_COUNT = "VOTING_INVALID_OFFSET_OR_COUNT"; enum VoterState { Absent, Yea, Nay, DelegateYea, DelegateNay } @@ -176,15 +176,40 @@ contract Voting is IForwarder, AragonApp { delegatedVoters[_delegate].addresses.length--; } - function getDelegatedVoters(address _delegate, uint96 offset, uint96 count) public view returns (address[] memory) { + function _getDelegatedVotersAt(address _delegate, uint256 _blockNumber, uint256 _offset, uint256 _count) internal view returns (address[], uint256[]) { require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); - require(count > 0 && offset >= 0 && offset + count <= delegatedVoters[_delegate].addresses.length, ERROR_INVALID_OFFSET_AND_COUNT); + uint256 length = delegatedVoters[_delegate].addresses.length; + if (length == 0) { + return (new address[](0), new uint256[](0)); + } + + require(_count > 0 && _count.add(_offset) <= length, ERROR_INVALID_OFFSET_OR_COUNT); - address[] memory result = new address[](count); - for (uint256 i = 0; i < count; ++i) { - result[i] = delegatedVoters[_delegate].addresses[offset + i]; + address[] memory votersList = new address[](_count); + uint256[] memory votingPowerList = new uint256[](_count); + for (uint256 i = 0; i < _count; ++i) { + address voter = delegatedVoters[_delegate].addresses[_offset + i]; + votersList[i] = voter; + votingPowerList[i] = token.balanceOfAt(voter, _blockNumber); } - return result; + return (votersList, votingPowerList); + } + + function getDelegatedVoters(address _delegate, uint256 _offset, uint256 _count) public view returns (address[], uint256[]) { + return _getDelegatedVotersAt(_delegate, block.number, _offset, _count); + } + + function getDelegatedVotersAtVote(address _delegate, uint256 _voteId, uint256 _offset, uint256 _count) public view voteExists(_voteId) returns (address[], uint256[], VoterState[]) { + Vote storage vote_ = votes[_voteId]; + address[] memory votersList; + uint256[] memory votingPowerList; + (votersList, votingPowerList) = _getDelegatedVotersAt(_delegate, vote_.snapshotBlock, _offset, _count); + + VoterState[] memory voterStateList = new VoterState[](votersList.length); + for (uint256 i = 0; i < votersList.length; ++i) { + voterStateList[i] = vote_.voters[votersList[i]]; + } + return (votersList, votingPowerList, voterStateList); } function getTotalDelegatedVotersCount(address _delegate) public view returns (uint256) { @@ -316,10 +341,10 @@ contract Voting is IForwarder, AragonApp { require(_hasEnoughVotingPower(vote_, voteFor_), ERROR_CAN_NOT_VOTE); require(_canVoteFor(msg.sender, voteFor_), ERROR_CAN_NOT_VOTE_FOR); require(!_hasVoted(_voteId, voteFor_), ERROR_DELEGATE_CANNOT_OVERWRITE_VOTE); - _vote(_voteId, _supports, voteFor_, true); + _vote(_voteId, _supports, voteFor_, true); } } - + /** * @notice Execute vote #`_voteId` diff --git a/apps/voting/test/voting.js b/apps/voting/test/voting.js index 4d4dfc130..1e521aaa0 100644 --- a/apps/voting/test/voting.js +++ b/apps/voting/test/voting.js @@ -926,7 +926,7 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d const delegate = await voting.getDelegate(holder29) assert.equal(delegate, delegate1, 'holder29 should have delegate1 as a delegate') - const delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 1) + const delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] assertArraysEqualAsSets(delegatedVoters, [holder29], 'delegate1 should be a delegate of holder29') }) @@ -967,7 +967,7 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d }) assertAmountOfEvents(tx, 'DelegateSet', {expectedAmount: 1}) - const delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 2) + const delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 2))[0] assertArraysEqualAsSets(delegatedVoters, [holder29, holder51], 'delegate1 should be a delegate of holder29 and holder51') }) }) From c3f85775b9d420edcbf8c29ec28eebd812240e4e Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 13 Feb 2024 14:09:53 +0100 Subject: [PATCH 018/100] refactor: optimize delegated voter removal --- apps/voting/contracts/Voting.sol | 16 +++++++++------- apps/voting/test/voting.js | 20 ++++++++++++++++++-- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 25fcc4d8c..88624ff2a 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -146,7 +146,7 @@ contract Voting is IForwarder, AragonApp { } _addDelegatedAddressFor(_delegate, msgSender); - delegates[msgSender] = Delegate(_delegate, uint96(delegatedVoters[_delegate].addresses.length - 1)); + delegates[msgSender] = Delegate(_delegate, uint96(delegatedVoters[_delegate].addresses.length.sub(1))); emit DelegateSet(msgSender, prevDelegate, _delegate); } @@ -168,11 +168,13 @@ contract Voting is IForwarder, AragonApp { function _removeDelegatedAddressFor(address _delegate, address _voter) internal { uint96 voterIndex = delegates[_voter].voterIndex; - - uint256 length = delegatedVoters[_delegate].addresses.length; - delegatedVoters[_delegate].addresses[voterIndex] = delegatedVoters[_delegate].addresses[length - 1]; - delegates[delegatedVoters[_delegate].addresses[length - 1]].voterIndex = voterIndex; - delete delegatedVoters[_delegate].addresses[length - 1]; + uint256 lastIndex = delegatedVoters[_delegate].addresses.length - 1; + address lastVoter = delegatedVoters[_delegate].addresses[lastIndex]; + if (voterIndex < lastIndex) { + delegatedVoters[_delegate].addresses[voterIndex] = lastVoter; + delegates[lastVoter].voterIndex = voterIndex; + } + delete delegatedVoters[_delegate].addresses[lastIndex]; delegatedVoters[_delegate].addresses.length--; } @@ -212,7 +214,7 @@ contract Voting is IForwarder, AragonApp { return (votersList, votingPowerList, voterStateList); } - function getTotalDelegatedVotersCount(address _delegate) public view returns (uint256) { + function getDelegatedVotersTotalCount(address _delegate) public view returns (uint256) { require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); return delegatedVoters[_delegate].addresses.length; } diff --git a/apps/voting/test/voting.js b/apps/voting/test/voting.js index 1e521aaa0..670b63d6a 100644 --- a/apps/voting/test/voting.js +++ b/apps/voting/test/voting.js @@ -930,7 +930,7 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d assertArraysEqualAsSets(delegatedVoters, [holder29], 'delegate1 should be a delegate of holder29') }) - it('voter can remove delegates', async () => { + it('voter can remove delegate', async () => { await voting.setDelegate(delegate1, {from: holder29}) const tx = await voting.removeDelegate({from: holder29}) @@ -938,9 +938,11 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d expectedArgs: {voter: holder29, previousDelegate: delegate1, newDelegate: ZERO_ADDRESS} }) assertAmountOfEvents(tx, 'DelegateSet', {expectedAmount: 1}) + const delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] + assertArraysEqualAsSets(delegatedVoters, [], 'delegate1 should not be a delegate of anyone') }) - it('voters can remove delegates', async () => { + it('voters can remove delegate', async () => { await voting.setDelegate(delegate1, {from: holder20}) await voting.setDelegate(delegate1, {from: holder29}) await voting.setDelegate(delegate1, {from: holder51}) @@ -956,6 +958,20 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d expectedArgs: {voter: holder51, previousDelegate: delegate1, newDelegate: ZERO_ADDRESS} }) assertAmountOfEvents(tx2, 'DelegateSet', {expectedAmount: 1}) + const delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] + assertArraysEqualAsSets(delegatedVoters, [holder20], 'delegate1 have only holder20 as a delegated voter') + }) + + it('voter can change delegate', async () => { + await voting.setDelegate(delegate1, {from: holder29}) + await voting.setDelegate(delegate2, {from: holder51}) + + await voting.setDelegate(delegate2, {from: holder29}) + + const delegatedVotersDelegate1 = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] + assertArraysEqualAsSets(delegatedVotersDelegate1, [], 'delegate1 should not be a delegate of anyone') + const delegatedVotersDelegate2 = (await voting.getDelegatedVoters(delegate2, 0, 2))[0] + assertArraysEqualAsSets(delegatedVotersDelegate2, [holder29, holder51], 'delegate2 should be a delegate of holder29 and holder51') }) it('delegate can manage several voters', async () => { From 59704174b1aa8740bc8cda6c8c7896728a753c42 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Wed, 14 Feb 2024 13:45:05 +0100 Subject: [PATCH 019/100] refactor: add max uint96 check for delegated voters array length --- apps/voting/contracts/Voting.sol | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 88624ff2a..e83325a07 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -24,6 +24,7 @@ contract Voting is IForwarder, AragonApp { uint64 public constant PCT_BASE = 10 ** 18; // 0% = 0; 1% = 10^16; 100% = 10^18 + uint256 private constant UINT_96_MAX = 0xFFFFFFFFFFFFFFFFFFFFFFFF; string private constant ERROR_NO_VOTE = "VOTING_NO_VOTE"; string private constant ERROR_INIT_PCTS = "VOTING_INIT_PCTS"; string private constant ERROR_CHANGE_SUPPORT_PCTS = "VOTING_CHANGE_SUPPORT_PCTS"; @@ -45,6 +46,7 @@ contract Voting is IForwarder, AragonApp { string private constant ERROR_DELEGATE_CANNOT_OVERWRITE_VOTE = "VOTING_DELEGATE_CANT_OVERWRITE"; string private constant ERROR_CAN_NOT_VOTE_FOR_MULTIPLE = "VOTING_CAN_NOT_VOTE_FOR_MULTIPLE"; string private constant ERROR_INVALID_OFFSET_OR_COUNT = "VOTING_INVALID_OFFSET_OR_COUNT"; + string private constant ERROR_MAX_DELEGATED_VOTERS_REACHED = "VOTING_MAX_DELEGATED_VOTERS_REACHED"; enum VoterState { Absent, Yea, Nay, DelegateYea, DelegateNay } @@ -163,6 +165,9 @@ contract Voting is IForwarder, AragonApp { } function _addDelegatedAddressFor(address _delegate, address _voter) internal { + uint256 length = delegatedVoters[_delegate].addresses.length; + require(length < UINT_96_MAX, ERROR_MAX_DELEGATED_VOTERS_REACHED); + delegatedVoters[_delegate].addresses.push(_voter); } From 3138944256902f90c9783d592b738b5ddbe5691b Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Wed, 14 Feb 2024 13:46:45 +0100 Subject: [PATCH 020/100] refactor: remove unused variables from Voting contract --- apps/voting/contracts/Voting.sol | 2 -- apps/voting/test/helpers/errors.js | 2 -- 2 files changed, 4 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index e83325a07..f7102fe0b 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -44,7 +44,6 @@ contract Voting is IForwarder, AragonApp { string private constant ERROR_SELF_DELEGATE = "VOTING_SELF_DELEGATE"; string private constant ERROR_DELEGATE_SAME_AS_PREV = "VOTING_DELEGATE_SAME_AS_PREV"; string private constant ERROR_DELEGATE_CANNOT_OVERWRITE_VOTE = "VOTING_DELEGATE_CANT_OVERWRITE"; - string private constant ERROR_CAN_NOT_VOTE_FOR_MULTIPLE = "VOTING_CAN_NOT_VOTE_FOR_MULTIPLE"; string private constant ERROR_INVALID_OFFSET_OR_COUNT = "VOTING_INVALID_OFFSET_OR_COUNT"; string private constant ERROR_MAX_DELEGATED_VOTERS_REACHED = "VOTING_MAX_DELEGATED_VOTERS_REACHED"; @@ -99,7 +98,6 @@ contract Voting is IForwarder, AragonApp { event ChangeObjectionPhaseTime(uint64 objectionPhaseTime); event DelegateSet(address indexed voter, address indexed previousDelegate, address indexed newDelegate); event CastVoteAsDelegate(uint256 indexed voteId, address indexed delegate, address indexed voter, bool supports, uint256 stake); - event VoteForMultipleSkippedFor(uint256 indexed voteId, address indexed delegate, address indexed skippedVoter, bool supports); modifier voteExists(uint256 _voteId) { require(_voteId < votesLength, ERROR_NO_VOTE); diff --git a/apps/voting/test/helpers/errors.js b/apps/voting/test/helpers/errors.js index 8cc694fb5..43b36a6dc 100644 --- a/apps/voting/test/helpers/errors.js +++ b/apps/voting/test/helpers/errors.js @@ -28,7 +28,5 @@ module.exports = makeErrorMappingProxy({ VOTING_DELEGATE_NOT_SET: 'VOTING_DELEGATE_NOT_SET', VOTING_SELF_DELEGATE: 'VOTING_SELF_DELEGATE', VOTING_DELEGATE_SAME_AS_PREV: 'VOTING_DELEGATE_SAME_AS_PREV', - VOTING_MAX_DELEGATED_VOTERS_REACHED: 'VOTING_MAX_DELEGATED_VOTERS_REACHED', VOTING_DELEGATE_CANT_OVERWRITE: 'VOTING_DELEGATE_CANT_OVERWRITE', - VOTING_CAN_NOT_VOTE_FOR_MULTIPLE: 'VOTING_CAN_NOT_VOTE_FOR_MULTIPLE', }) From 42bb8f3c0afc113195b9336c1151dc4a4bc532cb Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Wed, 14 Feb 2024 20:39:52 +0100 Subject: [PATCH 021/100] refactor: rebuild invariant checks for voteForMultiple, refactor voteFor --- apps/voting/contracts/Voting.sol | 71 ++++++++++++++++---------------- apps/voting/test/voting.js | 8 ++-- 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index f7102fe0b..00f3b685d 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -43,7 +43,6 @@ contract Voting is IForwarder, AragonApp { string private constant ERROR_DELEGATE_NOT_SET = "VOTING_DELEGATE_NOT_SET"; string private constant ERROR_SELF_DELEGATE = "VOTING_SELF_DELEGATE"; string private constant ERROR_DELEGATE_SAME_AS_PREV = "VOTING_DELEGATE_SAME_AS_PREV"; - string private constant ERROR_DELEGATE_CANNOT_OVERWRITE_VOTE = "VOTING_DELEGATE_CANT_OVERWRITE"; string private constant ERROR_INVALID_OFFSET_OR_COUNT = "VOTING_INVALID_OFFSET_OR_COUNT"; string private constant ERROR_MAX_DELEGATED_VOTERS_REACHED = "VOTING_MAX_DELEGATED_VOTERS_REACHED"; @@ -321,32 +320,28 @@ contract Voting is IForwarder, AragonApp { * @param _executesIfDecided_deprecated Whether the vote should execute its action if it becomes decided */ function vote(uint256 _voteId, bool _supports, bool _executesIfDecided_deprecated) external voteExists(_voteId) { - require(_canVote(_voteId, msg.sender), ERROR_CAN_NOT_VOTE); - require(_isProperPhaseToVote(_voteId, _supports), ERROR_CAN_NOT_VOTE); + require(_canParticipateInVote(_voteId, _supports), ERROR_CAN_NOT_VOTE); + require(_hasVotingPower(votes[_voteId], msg.sender), ERROR_NO_VOTING_POWER); _vote(_voteId, _supports, msg.sender, false); } - function voteFor(uint256 _voteId, bool _supports, address _voteFor) external voteExists(_voteId) { - Vote storage vote_ = votes[_voteId]; - require(_isVoteOpen(vote_), ERROR_CAN_NOT_VOTE); - require(_isProperPhaseToVote(_voteId, _supports), ERROR_CAN_NOT_VOTE); - require(_hasEnoughVotingPower(vote_, _voteFor), ERROR_CAN_NOT_VOTE); - require(_canVoteFor(msg.sender, _voteFor), ERROR_CAN_NOT_VOTE_FOR); - require(!_hasVoted(_voteId, _voteFor), ERROR_DELEGATE_CANNOT_OVERWRITE_VOTE); - _vote(_voteId, _supports, _voteFor, true); + function voteFor(uint256 _voteId, bool _supports, address _voter) external voteExists(_voteId) { + address[] memory voters = new address[](1); + voters[0] = _voter; + voteForMultiple(_voteId, _supports, voters); } - function voteForMultiple(uint256 _voteId, bool _supports, address[] _voteForList) external voteExists(_voteId) { + function voteForMultiple(uint256 _voteId, bool _supports, address[] _voters) public voteExists(_voteId) { + require(_canParticipateInVote(_voteId, _supports), ERROR_CAN_NOT_VOTE); + Vote storage vote_ = votes[_voteId]; - require(_isVoteOpen(vote_), ERROR_CAN_NOT_VOTE); - require(_isProperPhaseToVote(_voteId, _supports), ERROR_CAN_NOT_VOTE); - - for (uint256 i = 0; i < _voteForList.length; ++i) { - address voteFor_ = _voteForList[i]; - require(_hasEnoughVotingPower(vote_, voteFor_), ERROR_CAN_NOT_VOTE); - require(_canVoteFor(msg.sender, voteFor_), ERROR_CAN_NOT_VOTE_FOR); - require(!_hasVoted(_voteId, voteFor_), ERROR_DELEGATE_CANNOT_OVERWRITE_VOTE); - _vote(_voteId, _supports, voteFor_, true); + address msgSender = msg.sender; + + for (uint256 i = 0; i < _voters.length; ++i) { + address voter = _voters[i]; + require(_hasVotingPower(vote_, voter), ERROR_NO_VOTING_POWER); + require(_canVoteFor(vote_, msgSender, voter), ERROR_CAN_NOT_VOTE_FOR); + _vote(_voteId, _supports, voter, true); } } @@ -414,8 +409,9 @@ contract Voting is IForwarder, AragonApp { * @param _voter address of the voter to check * @return True if the given voter can participate in the main phase of a certain vote, false otherwise */ - function canVote(uint256 _voteId, address _voter) external view voteExists(_voteId) returns (bool) { - return _canVote(_voteId, _voter); + function canVote(uint256 _voteId, address _voter) public view voteExists(_voteId) returns (bool) { + Vote storage vote_ = votes[_voteId]; + return _canParticipateInVote(_voteId, false) && _hasVotingPower(vote_, _voter); } /** @@ -510,7 +506,7 @@ contract Voting is IForwarder, AragonApp { emit StartVote(voteId, msg.sender, _metadata); - if (_castVote && _canVote(voteId, msg.sender)) { + if (_castVote && canVote(voteId, msg.sender)) { _vote(voteId, true, msg.sender, false); } } @@ -608,28 +604,33 @@ contract Voting is IForwarder, AragonApp { * @dev Internal function to check if a voter can participate on a vote. It assumes the queried vote exists. * @return True if the given voter can participate a certain vote, false otherwise */ - function _canVote(uint256 _voteId, address _voter) internal view returns (bool) { + function _canParticipateInVote(uint256 _voteId, bool _supports) internal view returns (bool) { Vote storage vote_ = votes[_voteId]; - return _isVoteOpen(vote_) && _hasEnoughVotingPower(vote_, _voter); + return _isVoteOpen(vote_) && _isValidPhaseToVote(vote_, _supports); } - function _canVoteFor(address _delegate, address _voter) internal view returns (bool) { - require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); - require(_voter != address(0), ERROR_ZERO_ADDRESS_PASSED); + function _isDelegate(address _delegate, address _voter) internal view returns (bool) { + if (_delegate == address(0) || _voter == address(0)) { + return false; + } return delegates[_voter].delegate == _delegate; } - function _isProperPhaseToVote(uint256 _voteId, bool _supports) internal view returns (bool) { - return !_supports || _getVotePhase(votes[_voteId]) == VotePhase.Main; + function _canVoteFor(Vote storage _vote, address _delegate, address _voter) internal view returns (bool) { + return _isDelegate(_delegate, _voter) && !_hasVoted(_vote, _voter); + } + + function _isValidPhaseToVote(Vote storage _vote, bool _supports) internal view returns (bool) { + return !_supports || _getVotePhase(_vote) == VotePhase.Main; } - function _hasVoted(uint256 _voteId, address _voter) internal view returns (bool) { - VoterState state = votes[_voteId].voters[_voter]; + function _hasVoted(Vote storage _vote, address _voter) internal view returns (bool) { + VoterState state = _vote.voters[_voter]; return state == VoterState.Yea || state == VoterState.Nay; } - function _hasEnoughVotingPower(Vote storage vote_, address _voter) internal view returns (bool) { - return token.balanceOfAt(_voter, vote_.snapshotBlock) > 0; + function _hasVotingPower(Vote storage _vote, address _voter) internal view returns (bool) { + return token.balanceOfAt(_voter, _vote.snapshotBlock) > 0; } /** diff --git a/apps/voting/test/voting.js b/apps/voting/test/voting.js index 670b63d6a..0165eba66 100644 --- a/apps/voting/test/voting.js +++ b/apps/voting/test/voting.js @@ -103,7 +103,7 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d }) for (const decimals of [0, 2, 18, 26]) { - context(`normal token supply, ${decimals} decimals`, () => { + context(`normal token supply, ${decimals} decimals`, () => { const neededSupport = pct16(50) const minimumAcceptanceQuorum = pct16(20) @@ -361,7 +361,7 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d }) it('throws when non-holder votes', async () => { - await assertRevert(voting.vote(voteId, true, true, { from: nonHolder }), ERRORS.VOTING_CAN_NOT_VOTE) + await assertRevert(voting.vote(voteId, true, true, { from: nonHolder }), ERRORS.VOTING_NO_VOTING_POWER) }) it('throws when voting after voting closes', async () => { @@ -480,7 +480,7 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d await voting.vote(voteId, false, true, { from: holder29 }) await assertRevert( voting.voteForMultiple(voteId, false, [holder29, holder51], {from: delegate1}), - ERRORS.VOTING_DELEGATE_CANT_OVERWRITE + ERRORS.VOTING_CAN_NOT_VOTE_FOR ) }) @@ -510,7 +510,7 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d false, holder29, { from: delegate1 } - ), ERRORS.VOTING_DELEGATE_CANT_OVERWRITE + ), ERRORS.VOTING_CAN_NOT_VOTE_FOR ) }) }) From 569a600fee26d01c2dd91f0ce8ff0f6d3788885e Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Fri, 16 Feb 2024 09:47:34 +0100 Subject: [PATCH 022/100] refactor: update delegation getters and setters --- apps/voting/contracts/Voting.sol | 42 +++++++++++++------------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 00f3b685d..4f77a5cf2 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -43,7 +43,8 @@ contract Voting is IForwarder, AragonApp { string private constant ERROR_DELEGATE_NOT_SET = "VOTING_DELEGATE_NOT_SET"; string private constant ERROR_SELF_DELEGATE = "VOTING_SELF_DELEGATE"; string private constant ERROR_DELEGATE_SAME_AS_PREV = "VOTING_DELEGATE_SAME_AS_PREV"; - string private constant ERROR_INVALID_OFFSET_OR_COUNT = "VOTING_INVALID_OFFSET_OR_COUNT"; + string private constant ERROR_INVALID_LIMIT = "VOTING_INVALID_LIMIT"; + string private constant ERROR_INVALID_OFFSET = "VOTING_INVALID_OFFSET"; string private constant ERROR_MAX_DELEGATED_VOTERS_REACHED = "VOTING_MAX_DELEGATED_VOTERS_REACHED"; enum VoterState { Absent, Yea, Nay, DelegateYea, DelegateNay } @@ -143,9 +144,7 @@ contract Voting is IForwarder, AragonApp { if (prevDelegate != address(0)) { _removeDelegatedAddressFor(prevDelegate, msgSender); } - _addDelegatedAddressFor(_delegate, msgSender); - delegates[msgSender] = Delegate(_delegate, uint96(delegatedVoters[_delegate].addresses.length.sub(1))); emit DelegateSet(msgSender, prevDelegate, _delegate); } @@ -156,7 +155,7 @@ contract Voting is IForwarder, AragonApp { require(prevDelegate != address(0), ERROR_DELEGATE_NOT_SET); _removeDelegatedAddressFor(prevDelegate, msgSender); - delegates[msgSender] = Delegate(address(0), 0); + delete delegates[msgSender]; emit DelegateSet(msgSender, prevDelegate, address(0)); } @@ -166,6 +165,7 @@ contract Voting is IForwarder, AragonApp { require(length < UINT_96_MAX, ERROR_MAX_DELEGATED_VOTERS_REACHED); delegatedVoters[_delegate].addresses.push(_voter); + delegates[_voter] = Delegate(_delegate, uint96(delegatedVoters[_delegate].addresses.length.sub(1))); } function _removeDelegatedAddressFor(address _delegate, address _voter) internal { @@ -176,22 +176,22 @@ contract Voting is IForwarder, AragonApp { delegatedVoters[_delegate].addresses[voterIndex] = lastVoter; delegates[lastVoter].voterIndex = voterIndex; } - delete delegatedVoters[_delegate].addresses[lastIndex]; delegatedVoters[_delegate].addresses.length--; } - function _getDelegatedVotersAt(address _delegate, uint256 _blockNumber, uint256 _offset, uint256 _count) internal view returns (address[], uint256[]) { + function _getDelegatedVotersAt(address _delegate, uint256 _offset, uint256 _limit, uint256 _blockNumber) internal view returns (address[] memory votersList, uint256[] memory votingPowerList) { require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); + require(_limit > 0, ERROR_INVALID_LIMIT); uint256 length = delegatedVoters[_delegate].addresses.length; if (length == 0) { return (new address[](0), new uint256[](0)); } - - require(_count > 0 && _count.add(_offset) <= length, ERROR_INVALID_OFFSET_OR_COUNT); - - address[] memory votersList = new address[](_count); - uint256[] memory votingPowerList = new uint256[](_count); - for (uint256 i = 0; i < _count; ++i) { + require(_offset < length, ERROR_INVALID_OFFSET); + + uint256 returnCount = _offset.add(_limit) > length ? length.sub(_offset) : _limit; + votersList = new address[](returnCount); + votingPowerList = new uint256[](returnCount); + for (uint256 i = 0; i < returnCount; ++i) { address voter = delegatedVoters[_delegate].addresses[_offset + i]; votersList[i] = voter; votingPowerList[i] = token.balanceOfAt(voter, _blockNumber); @@ -199,24 +199,16 @@ contract Voting is IForwarder, AragonApp { return (votersList, votingPowerList); } - function getDelegatedVoters(address _delegate, uint256 _offset, uint256 _count) public view returns (address[], uint256[]) { - return _getDelegatedVotersAt(_delegate, block.number, _offset, _count); + function getDelegatedVoters(address _delegate, uint256 _offset, uint256 _limit) public view returns (address[] memory, uint256[] memory) { + return _getDelegatedVotersAt(_delegate, _offset, _limit, getBlockNumber64()); } - function getDelegatedVotersAtVote(address _delegate, uint256 _voteId, uint256 _offset, uint256 _count) public view voteExists(_voteId) returns (address[], uint256[], VoterState[]) { + function getDelegatedVotersAtVote(address _delegate, uint256 _offset, uint256 _limit, uint256 _voteId) public view voteExists(_voteId) returns (address[] memory, uint256[] memory) { Vote storage vote_ = votes[_voteId]; - address[] memory votersList; - uint256[] memory votingPowerList; - (votersList, votingPowerList) = _getDelegatedVotersAt(_delegate, vote_.snapshotBlock, _offset, _count); - - VoterState[] memory voterStateList = new VoterState[](votersList.length); - for (uint256 i = 0; i < votersList.length; ++i) { - voterStateList[i] = vote_.voters[votersList[i]]; - } - return (votersList, votingPowerList, voterStateList); + return _getDelegatedVotersAt(_delegate, _offset, _limit, vote_.snapshotBlock); } - function getDelegatedVotersTotalCount(address _delegate) public view returns (uint256) { + function getDelegatedVotersCount(address _delegate) public view returns (uint256) { require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); return delegatedVoters[_delegate].addresses.length; } From 8bacd24e0e4aa5721d17cb5b0c23c799edbc24b4 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Fri, 16 Feb 2024 09:58:49 +0100 Subject: [PATCH 023/100] refactor: remove _castVote argument from _newVote --- apps/voting/contracts/Voting.sol | 18 +++++++----------- .../voting/contracts/test/mocks/VotingMock.sol | 2 +- apps/voting/test/voting.js | 17 +++++++---------- 3 files changed, 15 insertions(+), 22 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 4f77a5cf2..6300c1b59 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -282,7 +282,7 @@ contract Voting is IForwarder, AragonApp { * @return voteId Id for newly created vote */ function newVote(bytes _executionScript, string _metadata) external auth(CREATE_VOTES_ROLE) returns (uint256 voteId) { - return _newVote(_executionScript, _metadata, true); + return _newVote(_executionScript, _metadata); } /** @@ -290,16 +290,16 @@ contract Voting is IForwarder, AragonApp { * @dev _executesIfDecided was deprecated to introduce a proper lock period between decision and execution. * @param _executionScript EVM script to be executed on approval * @param _metadata Vote metadata - * @param _castVote Whether to also cast newly created vote + * @param _castVote_deprecated Whether to also cast newly created vote * @param _executesIfDecided_deprecated Whether to also immediately execute newly created vote if decided * @return voteId id for newly created vote */ - function newVote(bytes _executionScript, string _metadata, bool _castVote, bool _executesIfDecided_deprecated) + function newVote(bytes _executionScript, string _metadata, bool _castVote_deprecated, bool _executesIfDecided_deprecated) external auth(CREATE_VOTES_ROLE) returns (uint256 voteId) { - return _newVote(_executionScript, _metadata, _castVote); + return _newVote(_executionScript, _metadata); } /** @@ -366,7 +366,7 @@ contract Voting is IForwarder, AragonApp { */ function forward(bytes _evmScript) public { require(canForward(msg.sender, _evmScript), ERROR_CAN_NOT_FORWARD); - _newVote(_evmScript, "", true); + _newVote(_evmScript, ""); } /** @@ -401,7 +401,7 @@ contract Voting is IForwarder, AragonApp { * @param _voter address of the voter to check * @return True if the given voter can participate in the main phase of a certain vote, false otherwise */ - function canVote(uint256 _voteId, address _voter) public view voteExists(_voteId) returns (bool) { + function canVote(uint256 _voteId, address _voter) external view voteExists(_voteId) returns (bool) { Vote storage vote_ = votes[_voteId]; return _canParticipateInVote(_voteId, false) && _hasVotingPower(vote_, _voter); } @@ -481,7 +481,7 @@ contract Voting is IForwarder, AragonApp { * @dev Internal function to create a new vote * @return voteId id for newly created vote */ - function _newVote(bytes _executionScript, string _metadata, bool _castVote) internal returns (uint256 voteId) { + function _newVote(bytes _executionScript, string _metadata) internal returns (uint256 voteId) { uint64 snapshotBlock = getBlockNumber64() - 1; // avoid double voting in this very block uint256 votingPower = token.totalSupplyAt(snapshotBlock); require(votingPower > 0, ERROR_NO_VOTING_POWER); @@ -497,10 +497,6 @@ contract Voting is IForwarder, AragonApp { vote_.executionScript = _executionScript; emit StartVote(voteId, msg.sender, _metadata); - - if (_castVote && canVote(voteId, msg.sender)) { - _vote(voteId, true, msg.sender, false); - } } /** diff --git a/apps/voting/contracts/test/mocks/VotingMock.sol b/apps/voting/contracts/test/mocks/VotingMock.sol index 16e2f4c2a..d025fa051 100644 --- a/apps/voting/contracts/test/mocks/VotingMock.sol +++ b/apps/voting/contracts/test/mocks/VotingMock.sol @@ -18,7 +18,7 @@ contract VotingMock is Voting, TimeHelpersMock { token.generateTokens(_holder, _tokenAmount); bytes memory noScript = new bytes(0); - voteId = _newVote(noScript, _metadata, false); + voteId = _newVote(noScript, _metadata); emit StartVote(voteId, msg.sender, _metadata); } } diff --git a/apps/voting/test/voting.js b/apps/voting/test/voting.js index 0165eba66..538eb3400 100644 --- a/apps/voting/test/voting.js +++ b/apps/voting/test/voting.js @@ -124,6 +124,7 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d const script = encodeCallScript([action, action, action]) const voteId = createdVoteId(await voting.newVote(script, '', { from: holder51 })) + await voting.vote(voteId, true, true, { from: holder51 }) await voting.mockIncreaseTime(votingDuration + 1) await voting.executeVote(voteId) @@ -132,30 +133,26 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d it('execution script can be empty', async () => { const voteId = createdVoteId(await voting.newVote(encodeCallScript([]), '', { from: holder51 })) + await voting.vote(voteId, true, true, { from: holder51 }) await voting.mockIncreaseTime(votingDuration + 1) await voting.executeVote(voteId) }) - it('can create newVote with extended API version', async () => { - let voteId = createdVoteId(await voting.methods['newVote(bytes,string,bool,bool)'](encodeCallScript([]), '', false, false, { from: holder51 })) - await voting.mockIncreaseTime(votingDuration + 1) - await assertRevert(voting.executeVote(voteId), ERRORS.VOTING_CAN_NOT_EXECUTE) - assert.equal(await voting.canExecute(voteId), false, 'should be non-executable') - - voteId = createdVoteId(await voting.methods['newVote(bytes,string,bool,bool)'](encodeCallScript([]), '', true, false, { from: holder51 })) - await voting.mockIncreaseTime(votingDuration + 1) - await voting.executeVote(voteId) - assert.equal(await voting.canExecute(voteId), false, 'should be in the executed state') + it('check castVote do nothing (deprecated)', async () => { + let voteId = createdVoteId(await voting.methods['newVote(bytes,string,bool,bool)'](encodeCallScript([]), '', true, false, { from: holder51 })) + assert.equal(await voting.getVoterState(voteId, holder51), VOTER_STATE.ABSENT, 'holder51 should not have voted') }) it('check executesIfDecided do nothing (deprecated)', async () => { let voteId = createdVoteId(await voting.methods['newVote(bytes,string,bool,bool)'](encodeCallScript([]), '', true, false, { from: holder51 })) + await voting.vote(voteId, true, true, { from: holder51 }) assert.equal(await voting.canExecute(voteId), false, 'should be in the unexecuted state') await voting.mockIncreaseTime(votingDuration + 1) await voting.executeVote(voteId) assert.equal(await voting.canExecute(voteId), false, 'should be in the executed state') voteId = createdVoteId(await voting.methods['newVote(bytes,string,bool,bool)'](encodeCallScript([]), '', true, true, { from: holder51 })) + await voting.vote(voteId, true, true, { from: holder51 }) assert.equal(await voting.canExecute(voteId), false, 'should be in the unexecuted state') await voting.mockIncreaseTime(votingDuration + 1) await voting.executeVote(voteId) From ae9ce255cb345f45f70a1a4b651fc8999d101a38 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Wed, 21 Feb 2024 18:11:42 +0100 Subject: [PATCH 024/100] feat: add getVoterStateMultiple --- apps/voting/contracts/Voting.sol | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 6300c1b59..4780cd125 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -218,6 +218,15 @@ contract Voting is IForwarder, AragonApp { return delegates[_voter].delegate; } + function getVoterStateMultiple(uint256 _voteId, address[] _voters) public view voteExists(_voteId) returns (VoterState[] memory voterStatesList) { + uint256 length = _voters.length; + voterStatesList = new VoterState[](length); + for (uint256 i = 0; i < length; ++i) { + voterStatesList[i] = votes[_voteId].voters[_voters[i]]; + } + return voterStatesList; + } + /** * @notice Change required support to `@formatPct(_supportRequiredPct)`% * @param _supportRequiredPct New required support @@ -360,7 +369,7 @@ contract Voting is IForwarder, AragonApp { } /** - * @notice Creates a vote to execute the desired action, and casts a support vote if possible + * @notice Creates a vote to execute the desired action * @dev IForwarder interface conformance * @param _evmScript Start vote with script */ From 82aaaf47e4c69cc676d6dfe37767e69ef19ddc13 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Wed, 21 Feb 2024 18:12:19 +0100 Subject: [PATCH 025/100] refactor: rename _isDelegate fn to _isDelegateFor --- apps/voting/contracts/Voting.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 4780cd125..3422d5b4b 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -606,7 +606,7 @@ contract Voting is IForwarder, AragonApp { return _isVoteOpen(vote_) && _isValidPhaseToVote(vote_, _supports); } - function _isDelegate(address _delegate, address _voter) internal view returns (bool) { + function _isDelegateFor(address _delegate, address _voter) internal view returns (bool) { if (_delegate == address(0) || _voter == address(0)) { return false; } @@ -614,7 +614,7 @@ contract Voting is IForwarder, AragonApp { } function _canVoteFor(Vote storage _vote, address _delegate, address _voter) internal view returns (bool) { - return _isDelegate(_delegate, _voter) && !_hasVoted(_vote, _voter); + return _isDelegateFor(_delegate, _voter) && !_hasVoted(_vote, _voter); } function _isValidPhaseToVote(Vote storage _vote, bool _supports) internal view returns (bool) { From 2c248f13974f3c2c3f7c442eb4699552840b0507 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Fri, 23 Feb 2024 18:47:09 +0100 Subject: [PATCH 026/100] feat: add length check to _removeDelegatedAddressFor --- apps/voting/contracts/Voting.sol | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 3422d5b4b..b7edecb8a 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -169,10 +169,12 @@ contract Voting is IForwarder, AragonApp { } function _removeDelegatedAddressFor(address _delegate, address _voter) internal { + uint256 length = delegatedVoters[_delegate].addresses.length; + require(length > 0, ERROR_DELEGATE_NOT_SET); + uint96 voterIndex = delegates[_voter].voterIndex; - uint256 lastIndex = delegatedVoters[_delegate].addresses.length - 1; - address lastVoter = delegatedVoters[_delegate].addresses[lastIndex]; - if (voterIndex < lastIndex) { + address lastVoter = delegatedVoters[_delegate].addresses[length - 1]; + if (voterIndex < length - 1) { delegatedVoters[_delegate].addresses[voterIndex] = lastVoter; delegates[lastVoter].voterIndex = voterIndex; } From 6de0d13eb913ba2df5c0e5dac243cf816b9f32af Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Fri, 23 Feb 2024 18:52:08 +0100 Subject: [PATCH 027/100] refactor: rename getVoterStateMultiple -> getVotersStateAtVote --- apps/voting/contracts/Voting.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index b7edecb8a..f877bf99d 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -220,7 +220,7 @@ contract Voting is IForwarder, AragonApp { return delegates[_voter].delegate; } - function getVoterStateMultiple(uint256 _voteId, address[] _voters) public view voteExists(_voteId) returns (VoterState[] memory voterStatesList) { + function getVotersStateAtVote(address[] _voters, uint256 _voteId) public view voteExists(_voteId) returns (VoterState[] memory voterStatesList) { uint256 length = _voters.length; voterStatesList = new VoterState[](length); for (uint256 i = 0; i < length; ++i) { From 02b11ee3d08330cf2b7216c7fc15a6f98587394c Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Mon, 26 Feb 2024 10:25:52 +0100 Subject: [PATCH 028/100] refactor: read vote outside of the loop in getVotersStateAtVote --- apps/voting/contracts/Voting.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index f877bf99d..d5a97a30d 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -223,8 +223,9 @@ contract Voting is IForwarder, AragonApp { function getVotersStateAtVote(address[] _voters, uint256 _voteId) public view voteExists(_voteId) returns (VoterState[] memory voterStatesList) { uint256 length = _voters.length; voterStatesList = new VoterState[](length); + Vote storage vote_ = votes[_voteId]; for (uint256 i = 0; i < length; ++i) { - voterStatesList[i] = votes[_voteId].voters[_voters[i]]; + voterStatesList[i] = vote_.voters[_voters[i]]; } return voterStatesList; } From 8476902de485c8282cb9576a16096a4f42169493 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Wed, 28 Feb 2024 15:28:42 +0100 Subject: [PATCH 029/100] refactor: update contract structure, add functions docs --- apps/voting/contracts/Voting.sol | 289 +++++++++++++++++++------------ 1 file changed, 176 insertions(+), 113 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index d5a97a30d..d925a6af1 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -129,107 +129,6 @@ contract Voting is IForwarder, AragonApp { objectionPhaseTime = _objectionPhaseTime; } - function setDelegate(address _delegate) public { - require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); - - address msgSender = msg.sender; - require(_delegate != msgSender, ERROR_SELF_DELEGATE); - - address prevDelegate = delegates[msgSender].delegate; - require(_delegate != prevDelegate, ERROR_DELEGATE_SAME_AS_PREV); - - uint256 votingPower = token.balanceOfAt(msgSender, getBlockNumber64() - 1); - require(votingPower > 0, ERROR_NO_VOTING_POWER); - - if (prevDelegate != address(0)) { - _removeDelegatedAddressFor(prevDelegate, msgSender); - } - _addDelegatedAddressFor(_delegate, msgSender); - - emit DelegateSet(msgSender, prevDelegate, _delegate); - } - - function removeDelegate() public { - address msgSender = msg.sender; - address prevDelegate = delegates[msgSender].delegate; - require(prevDelegate != address(0), ERROR_DELEGATE_NOT_SET); - - _removeDelegatedAddressFor(prevDelegate, msgSender); - delete delegates[msgSender]; - - emit DelegateSet(msgSender, prevDelegate, address(0)); - } - - function _addDelegatedAddressFor(address _delegate, address _voter) internal { - uint256 length = delegatedVoters[_delegate].addresses.length; - require(length < UINT_96_MAX, ERROR_MAX_DELEGATED_VOTERS_REACHED); - - delegatedVoters[_delegate].addresses.push(_voter); - delegates[_voter] = Delegate(_delegate, uint96(delegatedVoters[_delegate].addresses.length.sub(1))); - } - - function _removeDelegatedAddressFor(address _delegate, address _voter) internal { - uint256 length = delegatedVoters[_delegate].addresses.length; - require(length > 0, ERROR_DELEGATE_NOT_SET); - - uint96 voterIndex = delegates[_voter].voterIndex; - address lastVoter = delegatedVoters[_delegate].addresses[length - 1]; - if (voterIndex < length - 1) { - delegatedVoters[_delegate].addresses[voterIndex] = lastVoter; - delegates[lastVoter].voterIndex = voterIndex; - } - delegatedVoters[_delegate].addresses.length--; - } - - function _getDelegatedVotersAt(address _delegate, uint256 _offset, uint256 _limit, uint256 _blockNumber) internal view returns (address[] memory votersList, uint256[] memory votingPowerList) { - require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); - require(_limit > 0, ERROR_INVALID_LIMIT); - uint256 length = delegatedVoters[_delegate].addresses.length; - if (length == 0) { - return (new address[](0), new uint256[](0)); - } - require(_offset < length, ERROR_INVALID_OFFSET); - - uint256 returnCount = _offset.add(_limit) > length ? length.sub(_offset) : _limit; - votersList = new address[](returnCount); - votingPowerList = new uint256[](returnCount); - for (uint256 i = 0; i < returnCount; ++i) { - address voter = delegatedVoters[_delegate].addresses[_offset + i]; - votersList[i] = voter; - votingPowerList[i] = token.balanceOfAt(voter, _blockNumber); - } - return (votersList, votingPowerList); - } - - function getDelegatedVoters(address _delegate, uint256 _offset, uint256 _limit) public view returns (address[] memory, uint256[] memory) { - return _getDelegatedVotersAt(_delegate, _offset, _limit, getBlockNumber64()); - } - - function getDelegatedVotersAtVote(address _delegate, uint256 _offset, uint256 _limit, uint256 _voteId) public view voteExists(_voteId) returns (address[] memory, uint256[] memory) { - Vote storage vote_ = votes[_voteId]; - return _getDelegatedVotersAt(_delegate, _offset, _limit, vote_.snapshotBlock); - } - - function getDelegatedVotersCount(address _delegate) public view returns (uint256) { - require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); - return delegatedVoters[_delegate].addresses.length; - } - - function getDelegate(address _voter) public view returns (address) { - require(_voter != address(0), ERROR_ZERO_ADDRESS_PASSED); - return delegates[_voter].delegate; - } - - function getVotersStateAtVote(address[] _voters, uint256 _voteId) public view voteExists(_voteId) returns (VoterState[] memory voterStatesList) { - uint256 length = _voters.length; - voterStatesList = new VoterState[](length); - Vote storage vote_ = votes[_voteId]; - for (uint256 i = 0; i < length; ++i) { - voterStatesList[i] = vote_.voters[_voters[i]]; - } - return voterStatesList; - } - /** * @notice Change required support to `@formatPct(_supportRequiredPct)`% * @param _supportRequiredPct New required support @@ -329,12 +228,57 @@ contract Voting is IForwarder, AragonApp { _vote(_voteId, _supports, msg.sender, false); } - function voteFor(uint256 _voteId, bool _supports, address _voter) external voteExists(_voteId) { - address[] memory voters = new address[](1); - voters[0] = _voter; - voteForMultiple(_voteId, _supports, voters); + /** + * @notice Execute vote #`_voteId` + * @dev Initialization check is implicitly provided by `voteExists()` as new votes can only be + * created via `newVote(),` which requires initialization + * @param _voteId Id for vote + */ + function executeVote(uint256 _voteId) external voteExists(_voteId) { + _executeVote(_voteId); + } + + /** + * @notice Assign `_delegate` as the delegate for the sender + * @param _delegate address to delegate to + */ + function setDelegate(address _delegate) public { + require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); + + address msgSender = msg.sender; + require(_delegate != msgSender, ERROR_SELF_DELEGATE); + + address prevDelegate = delegates[msgSender].delegate; + require(_delegate != prevDelegate, ERROR_DELEGATE_SAME_AS_PREV); + + uint256 votingPower = token.balanceOfAt(msgSender, getBlockNumber64() - 1); + require(votingPower > 0, ERROR_NO_VOTING_POWER); + + if (prevDelegate != address(0)) { + _removeDelegatedAddressFor(prevDelegate, msgSender); + } + _addDelegatedAddressFor(_delegate, msgSender); + + emit DelegateSet(msgSender, prevDelegate, _delegate); } + function removeDelegate() public { + address msgSender = msg.sender; + address prevDelegate = delegates[msgSender].delegate; + require(prevDelegate != address(0), ERROR_DELEGATE_NOT_SET); + + _removeDelegatedAddressFor(prevDelegate, msgSender); + delete delegates[msgSender]; + + emit DelegateSet(msgSender, prevDelegate, address(0)); + } + + /** + * @notice Vote `_supports ? 'yes' : 'no'` in vote #`_voteId` on behalf of the `_voters` list + * @param _voteId Id for vote + * @param _supports Whether the delegate supports the vote + * @param _voters list of voters + */ function voteForMultiple(uint256 _voteId, bool _supports, address[] _voters) public voteExists(_voteId) { require(_canParticipateInVote(_voteId, _supports), ERROR_CAN_NOT_VOTE); @@ -349,15 +293,16 @@ contract Voting is IForwarder, AragonApp { } } - /** - * @notice Execute vote #`_voteId` - * @dev Initialization check is implicitly provided by `voteExists()` as new votes can only be - * created via `newVote(),` which requires initialization - * @param _voteId Id for vote - */ - function executeVote(uint256 _voteId) external voteExists(_voteId) { - _executeVote(_voteId); + * @notice Vote `_supports ? 'yes' : 'no'` in vote #`_voteId` on behalf of the `_voter` + * @param _voteId Id for vote + * @param _supports Whether the delegate supports the vote + * @param _voter address of the voter + */ + function voteFor(uint256 _voteId, bool _supports, address _voter) external voteExists(_voteId) { + address[] memory voters = new address[](1); + voters[0] = _voter; + voteForMultiple(_voteId, _supports, voters); } // Forwarding fns @@ -487,6 +432,65 @@ contract Voting is IForwarder, AragonApp { return votes[_voteId].voters[_voter]; } + /** + * @notice Return the sliced list of delegated voters for `_delegate` and their voting power at the current block + * @param _delegate the address of the delegate + * @param _offset the number of delegated voters from the start of the list to skip + * @param _limit the number of delegated voters to return + * @return the array of delegated voters + * @return the array of voting power of delegated voters + */ + function getDelegatedVoters(address _delegate, uint256 _offset, uint256 _limit) public view returns (address[] memory, uint256[] memory) { + return _getDelegatedVotersAt(_delegate, _offset, _limit, getBlockNumber64()); + } + + /** + * @notice Return the sliced list of delegated voters for `_delegate` and their voting power at the `_voteId` snapshot block + * @param _delegate the address of the delegate + * @param _offset the number of delegated voters from the start of the list to skip + * @param _limit the number of delegated voters to return + * @return the array of delegated voters + * @return the array of voting power of delegated voters + */ + function getDelegatedVotersAtVote(address _delegate, uint256 _offset, uint256 _limit, uint256 _voteId) public view voteExists(_voteId) returns (address[] memory, uint256[] memory) { + Vote storage vote_ = votes[_voteId]; + return _getDelegatedVotersAt(_delegate, _offset, _limit, vote_.snapshotBlock); + } + + /** + * @dev Return the number of delegated voters for the `_delegate` + * @param _delegate address of the delegate + * @return the number of delegated voters + */ + function getDelegatedVotersCount(address _delegate) public view returns (uint256) { + require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); + return delegatedVoters[_delegate].addresses.length; + } + + /** + * @notice Return the delegate address assigned to the `_voter` + * @param _voter the address of the voter + */ + function getDelegate(address _voter) public view returns (address) { + require(_voter != address(0), ERROR_ZERO_ADDRESS_PASSED); + return delegates[_voter].delegate; + } + + /** + * @notice Return the list of `VoterState` for the `_voters` for the vote #`_voteId` + * @param _voters the list of voters + * @param _voteId Vote identifier + */ + function getVotersStateAtVote(address[] _voters, uint256 _voteId) public view voteExists(_voteId) returns (VoterState[] memory voterStatesList) { + uint256 length = _voters.length; + voterStatesList = new VoterState[](length); + Vote storage vote_ = votes[_voteId]; + for (uint256 i = 0; i < length; ++i) { + voterStatesList[i] = vote_.voters[_voters[i]]; + } + return voterStatesList; + } + // Internal fns /** @@ -570,6 +574,65 @@ contract Voting is IForwarder, AragonApp { emit ExecuteVote(_voteId); } + /** + * @dev internal function to add the `_voter` to the list of delegated voters for the `_delegate` and update the delegate for the `_voter` + * @param _delegate address of the delegate + * @param _voter address of the voter + */ + function _addDelegatedAddressFor(address _delegate, address _voter) internal { + uint256 length = delegatedVoters[_delegate].addresses.length; + require(length < UINT_96_MAX, ERROR_MAX_DELEGATED_VOTERS_REACHED); + + delegatedVoters[_delegate].addresses.push(_voter); + delegates[_voter] = Delegate(_delegate, uint96(delegatedVoters[_delegate].addresses.length.sub(1))); + } + + /** + * @dev internal function to remove the `_voter` from the list of delegated voters for the `_delegate` + * @param _delegate address of the delegate + * @param _voter address of the voter + */ + function _removeDelegatedAddressFor(address _delegate, address _voter) internal { + uint256 length = delegatedVoters[_delegate].addresses.length; + require(length > 0, ERROR_DELEGATE_NOT_SET); + + uint96 voterIndex = delegates[_voter].voterIndex; + address lastVoter = delegatedVoters[_delegate].addresses[length - 1]; + if (voterIndex < length - 1) { + delegatedVoters[_delegate].addresses[voterIndex] = lastVoter; + delegates[lastVoter].voterIndex = voterIndex; + } + delegatedVoters[_delegate].addresses.length--; + } + + /** + * @notice Return the sliced list of delegated voters for `_delegate` and their voting power at the `_blockNumber` + * @param _delegate the address of the delegate + * @param _offset the number of delegated voters from the start of the list to skip + * @param _limit the number of delegated voters to return + * @return the array of delegated voters + * @return the array of voting power of delegated voters + */ + function _getDelegatedVotersAt(address _delegate, uint256 _offset, uint256 _limit, uint256 _blockNumber) internal view returns (address[] memory votersList, uint256[] memory votingPowerList) { + require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); + require(_limit > 0, ERROR_INVALID_LIMIT); + uint256 length = delegatedVoters[_delegate].addresses.length; + if (length == 0) { + return (new address[](0), new uint256[](0)); + } + require(_offset < length, ERROR_INVALID_OFFSET); + + uint256 returnCount = _offset.add(_limit) > length ? length.sub(_offset) : _limit; + votersList = new address[](returnCount); + votingPowerList = new uint256[](returnCount); + for (uint256 i = 0; i < returnCount; ++i) { + address voter = delegatedVoters[_delegate].addresses[_offset + i]; + votersList[i] = voter; + votingPowerList[i] = token.balanceOfAt(voter, _blockNumber); + } + return (votersList, votingPowerList); + } + /** * @dev Internal function to check if a vote can be executed. It assumes the queried vote exists. * @return True if the given vote can be executed, false otherwise From b8b155560e6752fadd9e48d60e411f6475c06a24 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Thu, 29 Feb 2024 18:34:41 +0100 Subject: [PATCH 030/100] refactor: split DelegateSet into two events - SetDelegate and RemoveDelegate --- apps/voting/contracts/Voting.sol | 9 +++++---- apps/voting/test/voting.js | 30 +++++++++++++++--------------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index d925a6af1..a711cfd39 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -96,7 +96,8 @@ contract Voting is IForwarder, AragonApp { event ChangeMinQuorum(uint64 minAcceptQuorumPct); event ChangeVoteTime(uint64 voteTime); event ChangeObjectionPhaseTime(uint64 objectionPhaseTime); - event DelegateSet(address indexed voter, address indexed previousDelegate, address indexed newDelegate); + event SetDelegate(address indexed voter, address indexed delegate); + event RemoveDelegate(address indexed voter, address indexed delegate); event CastVoteAsDelegate(uint256 indexed voteId, address indexed delegate, address indexed voter, bool supports, uint256 stake); modifier voteExists(uint256 _voteId) { @@ -259,7 +260,7 @@ contract Voting is IForwarder, AragonApp { } _addDelegatedAddressFor(_delegate, msgSender); - emit DelegateSet(msgSender, prevDelegate, _delegate); + emit SetDelegate(msgSender, _delegate); } function removeDelegate() public { @@ -270,7 +271,7 @@ contract Voting is IForwarder, AragonApp { _removeDelegatedAddressFor(prevDelegate, msgSender); delete delegates[msgSender]; - emit DelegateSet(msgSender, prevDelegate, address(0)); + emit RemoveDelegate(msgSender, prevDelegate); } /** @@ -621,7 +622,7 @@ contract Voting is IForwarder, AragonApp { return (new address[](0), new uint256[](0)); } require(_offset < length, ERROR_INVALID_OFFSET); - + uint256 returnCount = _offset.add(_limit) > length ? length.sub(_offset) : _limit; votersList = new address[](returnCount); votingPowerList = new uint256[](returnCount); diff --git a/apps/voting/test/voting.js b/apps/voting/test/voting.js index 538eb3400..919f5ac7b 100644 --- a/apps/voting/test/voting.js +++ b/apps/voting/test/voting.js @@ -915,10 +915,10 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d it('voter can set delegate', async () => { const tx = await voting.setDelegate(delegate1, {from: holder29}) - assertEvent(tx, 'DelegateSet', { - expectedArgs: {voter: holder29, previousDelegate: ZERO_ADDRESS, newDelegate: delegate1} + assertEvent(tx, 'SetDelegate', { + expectedArgs: {voter: holder29, delegate: delegate1} }) - assertAmountOfEvents(tx, 'DelegateSet', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'SetDelegate', {expectedAmount: 1}) const delegate = await voting.getDelegate(holder29) assert.equal(delegate, delegate1, 'holder29 should have delegate1 as a delegate') @@ -931,10 +931,10 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d await voting.setDelegate(delegate1, {from: holder29}) const tx = await voting.removeDelegate({from: holder29}) - assertEvent(tx, 'DelegateSet', { - expectedArgs: {voter: holder29, previousDelegate: delegate1, newDelegate: ZERO_ADDRESS} + assertEvent(tx, 'RemoveDelegate', { + expectedArgs: {voter: holder29, delegate: delegate1} }) - assertAmountOfEvents(tx, 'DelegateSet', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'RemoveDelegate', {expectedAmount: 1}) const delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] assertArraysEqualAsSets(delegatedVoters, [], 'delegate1 should not be a delegate of anyone') }) @@ -946,15 +946,15 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d const tx1 = await voting.removeDelegate({from: holder29}) - assertEvent(tx1, 'DelegateSet', { - expectedArgs: {voter: holder29, previousDelegate: delegate1, newDelegate: ZERO_ADDRESS} + assertEvent(tx1, 'RemoveDelegate', { + expectedArgs: {voter: holder29, delegate: delegate1} }) - assertAmountOfEvents(tx1, 'DelegateSet', {expectedAmount: 1}) + assertAmountOfEvents(tx1, 'RemoveDelegate', {expectedAmount: 1}) const tx2 = await voting.removeDelegate({from: holder51}) - assertEvent(tx2, 'DelegateSet', { - expectedArgs: {voter: holder51, previousDelegate: delegate1, newDelegate: ZERO_ADDRESS} + assertEvent(tx2, 'RemoveDelegate', { + expectedArgs: {voter: holder51, delegate: delegate1} }) - assertAmountOfEvents(tx2, 'DelegateSet', {expectedAmount: 1}) + assertAmountOfEvents(tx2, 'RemoveDelegate', {expectedAmount: 1}) const delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] assertArraysEqualAsSets(delegatedVoters, [holder20], 'delegate1 have only holder20 as a delegated voter') }) @@ -975,10 +975,10 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d await voting.setDelegate(delegate1, {from: holder29}) const tx = await voting.setDelegate(delegate1, {from: holder51}) - assertEvent(tx, 'DelegateSet', { - expectedArgs: {voter: holder51, previousDelegate: ZERO_ADDRESS, newDelegate: delegate1} + assertEvent(tx, 'SetDelegate', { + expectedArgs: {voter: holder51, delegate: delegate1} }) - assertAmountOfEvents(tx, 'DelegateSet', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'SetDelegate', {expectedAmount: 1}) const delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 2))[0] assertArraysEqualAsSets(delegatedVoters, [holder29, holder51], 'delegate1 should be a delegate of holder29 and holder51') From 5dd2bd04421d246adb4803d29ede8e0315eaa4d7 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Fri, 1 Mar 2024 12:46:22 +0100 Subject: [PATCH 031/100] refactor: update delegation related naming (#36) * fix: internal function types * refactor: rename holder -> voter, manager -> delegate * refactor: update delegation related naming in tests --- apps/voting/contracts/Voting.sol | 126 ++++++++++++++--------------- apps/voting/test/helpers/errors.js | 10 +-- apps/voting/test/voting.js | 85 ++++++++++--------- 3 files changed, 110 insertions(+), 111 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 627a729aa..8ff6134c6 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -23,7 +23,7 @@ contract Voting is IForwarder, AragonApp { bytes32 public constant UNSAFELY_MODIFY_VOTE_TIME_ROLE = keccak256("UNSAFELY_MODIFY_VOTE_TIME_ROLE"); uint64 public constant PCT_BASE = 10 ** 18; // 0% = 0; 1% = 10^16; 100% = 10^18 - uint64 public constant MAX_HOLDERS_PER_MANAGER = 1024; // some reasonable number to mitigate unbound loop issue + uint64 public constant MAX_VOTERS_PER_DELEGATE = 1024; // some reasonable number to mitigate unbound loop issue string private constant ERROR_NO_VOTE = "VOTING_NO_VOTE"; string private constant ERROR_INIT_PCTS = "VOTING_INIT_PCTS"; @@ -40,13 +40,13 @@ contract Voting is IForwarder, AragonApp { string private constant ERROR_INIT_OBJ_TIME_TOO_BIG = "VOTING_INIT_OBJ_TIME_TOO_BIG"; string private constant ERROR_CAN_NOT_VOTE_FOR = "VOTING_CAN_NOT_VOTE_FOR"; string private constant ERROR_ZERO_ADDRESS_PASSED = "VOTING_ZERO_ADDRESS_PASSED"; - string private constant ERROR_MANAGER_NOT_SET = "VOTING_MANAGER_NOT_SET"; - string private constant ERROR_SELF_MANAGER = "VOTING_SELF_MANAGER"; - string private constant ERROR_MANAGER_SAME_AS_PREV = "VOTING_MANAGER_SAME_AS_PREV"; - string private constant ERROR_MAX_MANAGED_VOTERS_REACHED = "VOTING_MAX_MANAGED_VOTERS_REACHED"; - string private constant ERROR_MANAGER_CANNOT_OVERWRITE_VOTE = "VOTING_MGR_CANT_OVERWRITE"; + string private constant ERROR_DELEGATE_NOT_SET = "VOTING_DELEGATE_NOT_SET"; + string private constant ERROR_SELF_DELEGATE = "VOTING_SELF_DELEGATE"; + string private constant ERROR_DELEGATE_SAME_AS_PREV = "VOTING_DELEGATE_SAME_AS_PREV"; + string private constant ERROR_MAX_DELEGATED_VOTERS_REACHED = "VOTING_MAX_DELEGATED_VOTERS_REACHED"; + string private constant ERROR_DELEGATE_CANNOT_OVERWRITE_VOTE = "VOTING_DELEGATE_CANT_OVERWRITE"; - enum VoterState { Absent, Yea, Nay, ManagerYea, ManagerNay } + enum VoterState { Absent, Yea, Nay, DelegateYea, DelegateNay } enum VotePhase { Main, Objection, Closed } @@ -63,7 +63,7 @@ contract Voting is IForwarder, AragonApp { mapping (address => VoterState) voters; } - struct ManagedAddressList { + struct DelegatedAddressList { address[] addresses; } @@ -77,10 +77,10 @@ contract Voting is IForwarder, AragonApp { uint256 public votesLength; uint64 public objectionPhaseTime; - // manager -> [managed holder address] - mapping(address => ManagedAddressList) private managedHolders; - // holder -> manager - mapping(address => address) private managers; + // delegate -> [delegated voter address] + mapping(address => DelegatedAddressList) private delegatedVoters; + // voter -> delegate + mapping(address => address) private delegates; event StartVote(uint256 indexed voteId, address indexed creator, string metadata); event CastVote(uint256 indexed voteId, address indexed voter, bool supports, uint256 stake); @@ -90,7 +90,7 @@ contract Voting is IForwarder, AragonApp { event ChangeMinQuorum(uint64 minAcceptQuorumPct); event ChangeVoteTime(uint64 voteTime); event ChangeObjectionPhaseTime(uint64 objectionPhaseTime); - event ManagerSet(address indexed holder, address indexed previousVotingManager, address indexed newVotingManager); + event DelegateSet(address indexed voter, address indexed previousDelegate, address indexed newDelegate); modifier voteExists(uint256 _voteId) { require(_voteId < votesLength, ERROR_NO_VOTE); @@ -125,75 +125,75 @@ contract Voting is IForwarder, AragonApp { /** * TODO: Calculate gas spending and add hint for more efficient look up in the array if needed */ - function setVotingManager(address _manager) public { - require(_manager != address(0), ERROR_ZERO_ADDRESS_PASSED); + function setDelegate(address _delegate) public { + require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); address msgSender = msg.sender; - require(_manager != msg.sender, ERROR_SELF_MANAGER); + require(_delegate != msgSender, ERROR_SELF_DELEGATE); - address prevManager = managers[msgSender]; - require(_manager != prevManager, ERROR_MANAGER_SAME_AS_PREV); + address prevDelegate = delegates[msgSender]; + require(_delegate != prevDelegate, ERROR_DELEGATE_SAME_AS_PREV); - if (prevManager != address(0)) { - _removeManagedAddressFor(prevManager, msgSender); + if (prevDelegate != address(0)) { + _removeDelegatedAddressFor(prevDelegate, msgSender); } - _addManagedAddressFor(_manager, msgSender); - managers[msgSender] = _manager; + _addDelegatedAddressFor(_delegate, msgSender); + delegates[msgSender] = _delegate; - emit ManagerSet(msgSender, prevManager, _manager); + emit DelegateSet(msgSender, prevDelegate, _delegate); } /** * TODO: Calculate gas spending and add hint for more efficient look up in the array if needed */ - function removeVotingManager() public { + function removeDelegate() public { address msgSender = msg.sender; - address prevManager = managers[msgSender]; - require(prevManager != address(0), ERROR_MANAGER_NOT_SET); + address prevDelegate = delegates[msgSender]; + require(prevDelegate != address(0), ERROR_DELEGATE_NOT_SET); - _removeManagedAddressFor(prevManager, msgSender); - managers[msgSender] = address(0); + _removeDelegatedAddressFor(prevDelegate, msgSender); + delegates[msgSender] = address(0); - emit ManagerSet(msgSender, prevManager, address(0)); + emit DelegateSet(msgSender, prevDelegate, address(0)); } - function _addManagedAddressFor(address _manager, address _holder) public { - require(_manager != address(0), ERROR_ZERO_ADDRESS_PASSED); - require(_holder != address(0), ERROR_ZERO_ADDRESS_PASSED); + function _addDelegatedAddressFor(address _delegate, address _voter) internal { + require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); + require(_voter != address(0), ERROR_ZERO_ADDRESS_PASSED); - uint256 length = managedHolders[_manager].addresses.length; - require(length < MAX_HOLDERS_PER_MANAGER, ERROR_MAX_MANAGED_VOTERS_REACHED); + uint256 length = delegatedVoters[_delegate].addresses.length; + require(length < MAX_VOTERS_PER_DELEGATE, ERROR_MAX_DELEGATED_VOTERS_REACHED); - managedHolders[_manager].addresses.push(_holder); + delegatedVoters[_delegate].addresses.push(_voter); } - function _removeManagedAddressFor(address _manager, address _holder) public { - require(_manager != address(0), ERROR_ZERO_ADDRESS_PASSED); - require(_holder != address(0), ERROR_ZERO_ADDRESS_PASSED); + function _removeDelegatedAddressFor(address _delegate, address _voter) internal { + require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); + require(_voter != address(0), ERROR_ZERO_ADDRESS_PASSED); - uint256 length = managedHolders[_manager].addresses.length; + uint256 length = delegatedVoters[_delegate].addresses.length; for (uint256 i = 0; i < length; i++) { - if (managedHolders[_manager].addresses[i] == _holder) { - managedHolders[_manager].addresses[i] = managedHolders[_manager].addresses[length - 1]; - delete managedHolders[_manager].addresses[length - 1]; - managedHolders[_manager].addresses.length--; + if (delegatedVoters[_delegate].addresses[i] == _voter) { + delegatedVoters[_delegate].addresses[i] = delegatedVoters[_delegate].addresses[length - 1]; + delete delegatedVoters[_delegate].addresses[length - 1]; + delegatedVoters[_delegate].addresses.length--; break; } } } - function getManagedVoters(address _manager) public view returns (address[] memory) { - require(_manager != address(0), ERROR_ZERO_ADDRESS_PASSED); - return managedHolders[_manager].addresses; + function getDelegatedVoters(address _delegate) public view returns (address[] memory) { + require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); + return delegatedVoters[_delegate].addresses; } - function getVotingManager(address _holder) public view returns (address) { - require(_holder != address(0), ERROR_ZERO_ADDRESS_PASSED); - return managers[_holder]; + function getDelegate(address _voter) public view returns (address) { + require(_voter != address(0), ERROR_ZERO_ADDRESS_PASSED); + return delegates[_voter]; } - // TODO: add getter allowing easily calculate compound total voting power for set of managed holders for particular voteId + // TODO: add getter allowing easily calculate compound total voting power for set of delegated voters for particular voteId /** * @notice Change required support to `@formatPct(_supportRequiredPct)`% @@ -303,7 +303,7 @@ contract Voting is IForwarder, AragonApp { /** * TODO: Update the voteForMultiple function to allow voting with the entire voting power - * of both self-managed address and managed addresses / write voteWithFullPower function + * of both self-delegated address and delegated addresses / write voteWithFullPower function */ function voteForMultiple(uint256 _voteId, bool _supports, address[] _voteForList) external voteExists(_voteId) { require(!_supports || _getVotePhase(votes[_voteId]) == VotePhase.Main, ERROR_CAN_NOT_VOTE); @@ -484,34 +484,34 @@ contract Voting is IForwarder, AragonApp { * @dev Internal function to cast a vote or object to. @dev It assumes that voter can support or object to the vote */ - function _vote(uint256 _voteId, bool _supports, address _voter, bool _isManager) internal { + function _vote(uint256 _voteId, bool _supports, address _voter, bool _isDelegate) internal { Vote storage vote_ = votes[_voteId]; // This could re-enter, though we can assume the governance token is not malicious uint256 voterStake = token.balanceOfAt(_voter, vote_.snapshotBlock); VoterState state = vote_.voters[_voter]; - // Voting manager can't overwrite holder vote - if (_isManager && (state == VoterState.Yea || state == VoterState.Nay)) { - revert(ERROR_MANAGER_CANNOT_OVERWRITE_VOTE); + // Delegate can't overwrite voter vote + if (_isDelegate && (state == VoterState.Yea || state == VoterState.Nay)) { + revert(ERROR_DELEGATE_CANNOT_OVERWRITE_VOTE); } // If voter had previously voted, decrease count - if (state == VoterState.Yea || state == VoterState.ManagerYea) { + if (state == VoterState.Yea || state == VoterState.DelegateYea) { vote_.yea = vote_.yea.sub(voterStake); - } else if (state == VoterState.Nay || state == VoterState.ManagerNay) { + } else if (state == VoterState.Nay || state == VoterState.DelegateNay) { vote_.nay = vote_.nay.sub(voterStake); } if (_supports) { vote_.yea = vote_.yea.add(voterStake); - vote_.voters[_voter] = _isManager ? VoterState.ManagerYea : VoterState.Yea; + vote_.voters[_voter] = _isDelegate ? VoterState.DelegateYea : VoterState.Yea; } else { vote_.nay = vote_.nay.add(voterStake); - vote_.voters[_voter] = _isManager ? VoterState.ManagerNay : VoterState.Nay; + vote_.voters[_voter] = _isDelegate ? VoterState.DelegateNay : VoterState.Nay; } - // TODO: consider to add an event indicates that manager have voted + // TODO: consider to add an event indicates that delegate have voted emit CastVote(_voteId, _voter, _supports, voterStake); if (_getVotePhase(vote_) == VotePhase.Objection) { @@ -580,10 +580,10 @@ contract Voting is IForwarder, AragonApp { return _isVoteOpen(vote_) && token.balanceOfAt(_voter, vote_.snapshotBlock) > 0; } - function _canVoteFor(address _manager, address _voter) internal view returns (bool) { - require(_manager != address(0), ERROR_ZERO_ADDRESS_PASSED); + function _canVoteFor(address _delegate, address _voter) internal view returns (bool) { + require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); require(_voter != address(0), ERROR_ZERO_ADDRESS_PASSED); - return managers[_voter] == _manager; + return delegates[_voter] == _delegate; } /** diff --git a/apps/voting/test/helpers/errors.js b/apps/voting/test/helpers/errors.js index 2b2ce1e4f..6e9e689d0 100644 --- a/apps/voting/test/helpers/errors.js +++ b/apps/voting/test/helpers/errors.js @@ -25,9 +25,9 @@ module.exports = makeErrorMappingProxy({ // Voting manager errors VOTING_CAN_NOT_VOTE_FOR: 'VOTING_CAN_NOT_VOTE_FOR', VOTING_ZERO_ADDRESS_PASSED: 'VOTING_ZERO_ADDRESS_PASSED', - VOTING_MANAGER_NOT_SET: 'VOTING_MANAGER_NOT_SET', - VOTING_SELF_MANAGER: 'VOTING_SELF_MANAGER', - VOTING_MANAGER_SAME_AS_PREV: 'VOTING_MANAGER_SAME_AS_PREV', - VOTING_MAX_MANAGED_VOTERS_REACHED: 'VOTING_MAX_MANAGED_VOTERS_REACHED', - VOTING_MGR_CANT_OVERWRITE: 'VOTING_MGR_CANT_OVERWRITE', + VOTING_DELEGATE_NOT_SET: 'VOTING_DELEGATE_NOT_SET', + VOTING_SELF_DELEGATE: 'VOTING_SELF_DELEGATE', + VOTING_DELEGATE_SAME_AS_PREV: 'VOTING_DELEGATE_SAME_AS_PREV', + VOTING_MAX_DELEGATED_VOTERS_REACHED: 'VOTING_MAX_DELEGATED_VOTERS_REACHED', + VOTING_DELEGATE_CANT_OVERWRITE: 'VOTING_DELEGATE_CANT_OVERWRITE', }) diff --git a/apps/voting/test/voting.js b/apps/voting/test/voting.js index 39a2c4f6f..cabcf3ab3 100644 --- a/apps/voting/test/voting.js +++ b/apps/voting/test/voting.js @@ -12,13 +12,12 @@ const ExecutionTarget = artifacts.require('ExecutionTarget') const createdVoteId = receipt => getEventArgument(receipt, 'StartVote', 'voteId') -const VOTER_STATE = ['ABSENT', 'YEA', 'NAY', 'MANAGER_YEA', 'MANAGER_NAY'].reduce((state, key, index) => { +const VOTER_STATE = ['ABSENT', 'YEA', 'NAY', 'DELEGATE_YEA', 'DELEGATE_NAY'].reduce((state, key, index) => { state[key] = index; return state; }, {}) - -contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, manager1, manager2, nonHolder]) => { +contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, delegate1, delegate2, nonHolder]) => { let votingBase, voting, token, executionTarget, aclP let CREATE_VOTES_ROLE, MODIFY_SUPPORT_ROLE, MODIFY_QUORUM_ROLE, UNSAFELY_MODIFY_VOTE_TIME_ROLE @@ -427,8 +426,8 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, m await token.generateTokens(holder29, bigExp(29, decimals)) await token.generateTokens(holder51, bigExp(51, decimals)) await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, 0) - await voting.setVotingManager(manager1, {from: holder29}) - await voting.setVotingManager(manager1, {from: holder51}) + await voting.setDelegate(delegate1, {from: holder29}) + await voting.setDelegate(delegate1, {from: holder51}) executionTarget = await ExecutionTarget.new() @@ -442,8 +441,8 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, m }) - it('manager can vote for holder', async () => { - const tx = await voting.voteFor(voteId, false, holder29, {from: manager1}) + it('delegate can vote for voter', async () => { + const tx = await voting.voteFor(voteId, false, holder29, {from: delegate1}) assertEvent(tx, 'CastVote', {expectedArgs: {voteId: voteId, voter: holder29, supports: false}}) assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) assertAmountOfEvents(tx, 'CastObjection', {expectedAmount: 0}) @@ -452,11 +451,11 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, m const voterState = await voting.getVoterState(voteId, holder29) assertBn(state[7], bigExp(29, decimals), 'nay vote should have been counted') - assert.equal(voterState, VOTER_STATE.MANAGER_NAY, 'holder29 should have manager nay voter status') + assert.equal(voterState, VOTER_STATE.DELEGATE_NAY, 'holder29 should have delegate nay voter status') }) - it('manager can vote for both holders', async () => { - const tx = await voting.voteForMultiple(voteId, false, [holder29, holder51], {from: manager1}) + it('delegate can vote for both voters', async () => { + const tx = await voting.voteForMultiple(voteId, false, [holder29, holder51], {from: delegate1}) assertEvent(tx, 'CastVote', {expectedArgs: {voteId: voteId, voter: holder29, supports: false}}) assertEvent(tx, 'CastVote', {index: 1, expectedArgs: {voteId: voteId, voter: holder51, supports: false}}) assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 2}) @@ -466,14 +465,14 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, m assertBn(state[7], bigExp(80, decimals), 'nay vote should have been counted') const voterState29 = await voting.getVoterState(voteId, holder29) - assert.equal(voterState29, VOTER_STATE.MANAGER_NAY, 'holder29 should have manager nay voter status') + assert.equal(voterState29, VOTER_STATE.DELEGATE_NAY, 'holder29 should have delegate nay voter status') const voterState51 = await voting.getVoterState(voteId, holder51) - assert.equal(voterState51, VOTER_STATE.MANAGER_NAY, 'holder51 should have manager nay voter status') + assert.equal(voterState51, VOTER_STATE.DELEGATE_NAY, 'holder51 should have delegate nay voter status') }) - it(`holder can overwrite manager's vote`, async () => { - await voting.voteFor(voteId, false, holder29, {from: manager1}) + it(`voter can overwrite delegate's vote`, async () => { + await voting.voteFor(voteId, false, holder29, {from: delegate1}) const tx = await voting.vote(voteId, true, true, {from: holder29}) assertEvent(tx, 'CastVote', {expectedArgs: {voteId: voteId, voter: holder29, supports: true}}) @@ -488,8 +487,8 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, m assert.equal(voterState29, VOTER_STATE.YEA, 'holder29 should have yea voter status') }) - it(`manager can't overwrite holder's vote`, async () => { - await voting.voteFor(voteId, false, holder29, {from: manager1}) + it(`delegate can't overwrite voter's vote`, async () => { + await voting.voteFor(voteId, false, holder29, {from: delegate1}) await voting.vote(voteId, true, true, {from: holder29}) await assertRevert( @@ -497,8 +496,8 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, m voteId, false, holder29, - { from: manager1 } - ), ERRORS.VOTING_MGR_CANT_OVERWRITE + { from: delegate1 } + ), ERRORS.VOTING_DELEGATE_CANT_OVERWRITE ) }) }) @@ -888,7 +887,7 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, m }) }) - context('voting manager', () => { + context('voting delegate', () => { const neededSupport = pct16(50) const minimumAcceptanceQuorum = pct16(20) const decimals = 18 @@ -904,41 +903,41 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, m executionTarget = await ExecutionTarget.new() }) - it('holder can set voting manager', async () => { - const tx = await voting.setVotingManager(manager1, {from: holder29}) - assertEvent(tx, 'ManagerSet', { - expectedArgs: {holder: holder29, previousVotingManager: ZERO_ADDRESS, newVotingManager: manager1} + it('voter can set delegate', async () => { + const tx = await voting.setDelegate(delegate1, {from: holder29}) + assertEvent(tx, 'DelegateSet', { + expectedArgs: {voter: holder29, previousDelegate: ZERO_ADDRESS, newDelegate: delegate1} }) - assertAmountOfEvents(tx, 'ManagerSet', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'DelegateSet', {expectedAmount: 1}) - const manager = await voting.getVotingManager(holder29) - assert.equal(manager, manager1, 'holder29 should have manager1 as a voting manager') + const delegate = await voting.getDelegate(holder29) + assert.equal(delegate, delegate1, 'holder29 should have delegate1 as a delegate') - const managedHolders = await voting.getManagedVoters(manager1) - assertArraysEqualAsSets(managedHolders, [holder29], 'manager1 should manage holder29') + const delegatedVoters = await voting.getDelegatedVoters(delegate1) + assertArraysEqualAsSets(delegatedVoters, [holder29], 'delegate1 should be a delegate of holder29') }) - it('holder can remove voting managers', async () => { - await voting.setVotingManager(manager1, {from: holder29}) + it('voter can remove delegates', async () => { + await voting.setDelegate(delegate1, {from: holder29}) - const tx = await voting.removeVotingManager({from: holder29}) - assertEvent(tx, 'ManagerSet', { - expectedArgs: {holder: holder29, previousVotingManager: manager1, newVotingManager: ZERO_ADDRESS} + const tx = await voting.removeDelegate({from: holder29}) + assertEvent(tx, 'DelegateSet', { + expectedArgs: {voter: holder29, previousDelegate: delegate1, newDelegate: ZERO_ADDRESS} }) - assertAmountOfEvents(tx, 'ManagerSet', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'DelegateSet', {expectedAmount: 1}) }) - it('manager can manage several holders', async () => { - await voting.setVotingManager(manager1, {from: holder29}) + it('delegate can manage several voters', async () => { + await voting.setDelegate(delegate1, {from: holder29}) - const tx = await voting.setVotingManager(manager1, {from: holder51}) - assertEvent(tx, 'ManagerSet', { - expectedArgs: {holder: holder51, previousVotingManager: ZERO_ADDRESS, newVotingManager: manager1} + const tx = await voting.setDelegate(delegate1, {from: holder51}) + assertEvent(tx, 'DelegateSet', { + expectedArgs: {voter: holder51, previousDelegate: ZERO_ADDRESS, newDelegate: delegate1} }) - assertAmountOfEvents(tx, 'ManagerSet', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'DelegateSet', {expectedAmount: 1}) - const managedHolders = await voting.getManagedVoters(manager1) - assertArraysEqualAsSets(managedHolders, [holder29, holder51], 'manager1 should manage holder29 and holder51') + const delegatedVoters = await voting.getDelegatedVoters(delegate1) + assertArraysEqualAsSets(delegatedVoters, [holder29, holder51], 'delegate1 should be a delegate of holder29 and holder51') }) }) -}) +}) \ No newline at end of file From f02be61392ee55b182ab9f07026d94f5863cde3e Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Fri, 1 Mar 2024 13:47:01 +0100 Subject: [PATCH 032/100] style: fix formatting --- apps/voting/contracts/Voting.sol | 615 +++---- apps/voting/test/voting.js | 2716 +++++++++++------------------- 2 files changed, 1165 insertions(+), 2166 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index d079f8f95..a711cfd39 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -12,66 +12,44 @@ import "@aragon/os/contracts/lib/math/SafeMath64.sol"; import "@aragon/minime/contracts/MiniMeToken.sol"; + contract Voting is IForwarder, AragonApp { using SafeMath for uint256; using SafeMath64 for uint64; bytes32 public constant CREATE_VOTES_ROLE = keccak256("CREATE_VOTES_ROLE"); - bytes32 public constant MODIFY_SUPPORT_ROLE = - keccak256("MODIFY_SUPPORT_ROLE"); - bytes32 public constant MODIFY_QUORUM_ROLE = - keccak256("MODIFY_QUORUM_ROLE"); - bytes32 public constant UNSAFELY_MODIFY_VOTE_TIME_ROLE = - keccak256("UNSAFELY_MODIFY_VOTE_TIME_ROLE"); + bytes32 public constant MODIFY_SUPPORT_ROLE = keccak256("MODIFY_SUPPORT_ROLE"); + bytes32 public constant MODIFY_QUORUM_ROLE = keccak256("MODIFY_QUORUM_ROLE"); + bytes32 public constant UNSAFELY_MODIFY_VOTE_TIME_ROLE = keccak256("UNSAFELY_MODIFY_VOTE_TIME_ROLE"); uint64 public constant PCT_BASE = 10 ** 18; // 0% = 0; 1% = 10^16; 100% = 10^18 uint256 private constant UINT_96_MAX = 0xFFFFFFFFFFFFFFFFFFFFFFFF; string private constant ERROR_NO_VOTE = "VOTING_NO_VOTE"; string private constant ERROR_INIT_PCTS = "VOTING_INIT_PCTS"; - string private constant ERROR_CHANGE_SUPPORT_PCTS = - "VOTING_CHANGE_SUPPORT_PCTS"; - string private constant ERROR_CHANGE_QUORUM_PCTS = - "VOTING_CHANGE_QUORUM_PCTS"; - string private constant ERROR_INIT_SUPPORT_TOO_BIG = - "VOTING_INIT_SUPPORT_TOO_BIG"; - string private constant ERROR_CHANGE_SUPPORT_TOO_BIG = - "VOTING_CHANGE_SUPP_TOO_BIG"; + string private constant ERROR_CHANGE_SUPPORT_PCTS = "VOTING_CHANGE_SUPPORT_PCTS"; + string private constant ERROR_CHANGE_QUORUM_PCTS = "VOTING_CHANGE_QUORUM_PCTS"; + string private constant ERROR_INIT_SUPPORT_TOO_BIG = "VOTING_INIT_SUPPORT_TOO_BIG"; + string private constant ERROR_CHANGE_SUPPORT_TOO_BIG = "VOTING_CHANGE_SUPP_TOO_BIG"; string private constant ERROR_CAN_NOT_VOTE = "VOTING_CAN_NOT_VOTE"; string private constant ERROR_CAN_NOT_EXECUTE = "VOTING_CAN_NOT_EXECUTE"; string private constant ERROR_CAN_NOT_FORWARD = "VOTING_CAN_NOT_FORWARD"; string private constant ERROR_NO_VOTING_POWER = "VOTING_NO_VOTING_POWER"; - string private constant ERROR_CHANGE_VOTE_TIME = - "VOTING_VOTE_TIME_TOO_SMALL"; - string private constant ERROR_CHANGE_OBJECTION_TIME = - "VOTING_OBJ_TIME_TOO_BIG"; - string private constant ERROR_INIT_OBJ_TIME_TOO_BIG = - "VOTING_INIT_OBJ_TIME_TOO_BIG"; + string private constant ERROR_CHANGE_VOTE_TIME = "VOTING_VOTE_TIME_TOO_SMALL"; + string private constant ERROR_CHANGE_OBJECTION_TIME = "VOTING_OBJ_TIME_TOO_BIG"; + string private constant ERROR_INIT_OBJ_TIME_TOO_BIG = "VOTING_INIT_OBJ_TIME_TOO_BIG"; string private constant ERROR_CAN_NOT_VOTE_FOR = "VOTING_CAN_NOT_VOTE_FOR"; - string private constant ERROR_ZERO_ADDRESS_PASSED = - "VOTING_ZERO_ADDRESS_PASSED"; + string private constant ERROR_ZERO_ADDRESS_PASSED = "VOTING_ZERO_ADDRESS_PASSED"; string private constant ERROR_DELEGATE_NOT_SET = "VOTING_DELEGATE_NOT_SET"; string private constant ERROR_SELF_DELEGATE = "VOTING_SELF_DELEGATE"; - string private constant ERROR_DELEGATE_SAME_AS_PREV = - "VOTING_DELEGATE_SAME_AS_PREV"; + string private constant ERROR_DELEGATE_SAME_AS_PREV = "VOTING_DELEGATE_SAME_AS_PREV"; string private constant ERROR_INVALID_LIMIT = "VOTING_INVALID_LIMIT"; string private constant ERROR_INVALID_OFFSET = "VOTING_INVALID_OFFSET"; - string private constant ERROR_MAX_DELEGATED_VOTERS_REACHED = - "VOTING_MAX_DELEGATED_VOTERS_REACHED"; + string private constant ERROR_MAX_DELEGATED_VOTERS_REACHED = "VOTING_MAX_DELEGATED_VOTERS_REACHED"; - enum VoterState { - Absent, - Yea, - Nay, - DelegateYea, - DelegateNay - } + enum VoterState { Absent, Yea, Nay, DelegateYea, DelegateNay } - enum VotePhase { - Main, - Objection, - Closed - } + enum VotePhase { Main, Objection, Closed } struct Vote { bool executed; @@ -83,7 +61,7 @@ contract Voting is IForwarder, AragonApp { uint256 nay; uint256 votingPower; bytes executionScript; - mapping(address => VoterState) voters; + mapping (address => VoterState) voters; } struct DelegatedAddressList { @@ -101,7 +79,7 @@ contract Voting is IForwarder, AragonApp { uint64 public voteTime; // We are mimicing an array, we use a mapping instead to make app upgrade more graceful - mapping(uint256 => Vote) internal votes; + mapping (uint256 => Vote) internal votes; uint256 public votesLength; uint64 public objectionPhaseTime; @@ -110,22 +88,9 @@ contract Voting is IForwarder, AragonApp { // voter -> delegate mapping(address => Delegate) private delegates; - event StartVote( - uint256 indexed voteId, - address indexed creator, - string metadata - ); - event CastVote( - uint256 indexed voteId, - address indexed voter, - bool supports, - uint256 stake - ); - event CastObjection( - uint256 indexed voteId, - address indexed voter, - uint256 stake - ); + event StartVote(uint256 indexed voteId, address indexed creator, string metadata); + event CastVote(uint256 indexed voteId, address indexed voter, bool supports, uint256 stake); + event CastObjection(uint256 indexed voteId, address indexed voter, uint256 stake); event ExecuteVote(uint256 indexed voteId); event ChangeSupportRequired(uint64 supportRequiredPct); event ChangeMinQuorum(uint64 minAcceptQuorumPct); @@ -133,13 +98,7 @@ contract Voting is IForwarder, AragonApp { event ChangeObjectionPhaseTime(uint64 objectionPhaseTime); event SetDelegate(address indexed voter, address indexed delegate); event RemoveDelegate(address indexed voter, address indexed delegate); - event CastVoteAsDelegate( - uint256 indexed voteId, - address indexed delegate, - address indexed voter, - bool supports, - uint256 stake - ); + event CastVoteAsDelegate(uint256 indexed voteId, address indexed delegate, address indexed voter, bool supports, uint256 stake); modifier voteExists(uint256 _voteId) { require(_voteId < votesLength, ERROR_NO_VOTE); @@ -147,20 +106,17 @@ contract Voting is IForwarder, AragonApp { } /** - * @notice Initialize Voting app with `_token.symbol(): string` for governance, minimum support of `@formatPct(_supportRequiredPct)`%, minimum acceptance quorum of `@formatPct(_minAcceptQuorumPct)`%, and a voting duration of `@transformTime(_voteTime)` - * @param _token MiniMeToken Address that will be used as governance token - * @param _supportRequiredPct Percentage of yeas in casted votes for a vote to succeed (expressed as a percentage of 10^18; eg. 10^16 = 1%, 10^18 = 100%) - * @param _minAcceptQuorumPct Percentage of yeas in total possible votes for a vote to succeed (expressed as a percentage of 10^18; eg. 10^16 = 1%, 10^18 = 100%) - * @param _voteTime Total duration of voting in seconds. - * @param _objectionPhaseTime The duration of the objection vote phase, i.e. seconds that a vote will be open after the main vote phase ends for token holders to object to the outcome. Main phase duration is calculated as `voteTime - objectionPhaseTime`. - */ - function initialize( - MiniMeToken _token, - uint64 _supportRequiredPct, - uint64 _minAcceptQuorumPct, - uint64 _voteTime, - uint64 _objectionPhaseTime - ) external onlyInit { + * @notice Initialize Voting app with `_token.symbol(): string` for governance, minimum support of `@formatPct(_supportRequiredPct)`%, minimum acceptance quorum of `@formatPct(_minAcceptQuorumPct)`%, and a voting duration of `@transformTime(_voteTime)` + * @param _token MiniMeToken Address that will be used as governance token + * @param _supportRequiredPct Percentage of yeas in casted votes for a vote to succeed (expressed as a percentage of 10^18; eg. 10^16 = 1%, 10^18 = 100%) + * @param _minAcceptQuorumPct Percentage of yeas in total possible votes for a vote to succeed (expressed as a percentage of 10^18; eg. 10^16 = 1%, 10^18 = 100%) + * @param _voteTime Total duration of voting in seconds. + * @param _objectionPhaseTime The duration of the objection vote phase, i.e. seconds that a vote will be open after the main vote phase ends for token holders to object to the outcome. Main phase duration is calculated as `voteTime - objectionPhaseTime`. + */ + function initialize(MiniMeToken _token, uint64 _supportRequiredPct, uint64 _minAcceptQuorumPct, uint64 _voteTime, uint64 _objectionPhaseTime) + external + onlyInit + { initialized(); require(_minAcceptQuorumPct <= _supportRequiredPct, ERROR_INIT_PCTS); @@ -175,22 +131,14 @@ contract Voting is IForwarder, AragonApp { } /** - * @notice Change required support to `@formatPct(_supportRequiredPct)`% - * @param _supportRequiredPct New required support - */ - function changeSupportRequiredPct( - uint64 _supportRequiredPct - ) + * @notice Change required support to `@formatPct(_supportRequiredPct)`% + * @param _supportRequiredPct New required support + */ + function changeSupportRequiredPct(uint64 _supportRequiredPct) external - authP( - MODIFY_SUPPORT_ROLE, - arr(uint256(_supportRequiredPct), uint256(supportRequiredPct)) - ) + authP(MODIFY_SUPPORT_ROLE, arr(uint256(_supportRequiredPct), uint256(supportRequiredPct))) { - require( - minAcceptQuorumPct <= _supportRequiredPct, - ERROR_CHANGE_SUPPORT_PCTS - ); + require(minAcceptQuorumPct <= _supportRequiredPct, ERROR_CHANGE_SUPPORT_PCTS); require(_supportRequiredPct < PCT_BASE, ERROR_CHANGE_SUPPORT_TOO_BIG); supportRequiredPct = _supportRequiredPct; @@ -198,34 +146,27 @@ contract Voting is IForwarder, AragonApp { } /** - * @notice Change minimum acceptance quorum to `@formatPct(_minAcceptQuorumPct)`% - * @param _minAcceptQuorumPct New acceptance quorum - */ - function changeMinAcceptQuorumPct( - uint64 _minAcceptQuorumPct - ) + * @notice Change minimum acceptance quorum to `@formatPct(_minAcceptQuorumPct)`% + * @param _minAcceptQuorumPct New acceptance quorum + */ + function changeMinAcceptQuorumPct(uint64 _minAcceptQuorumPct) external - authP( - MODIFY_QUORUM_ROLE, - arr(uint256(_minAcceptQuorumPct), uint256(minAcceptQuorumPct)) - ) + authP(MODIFY_QUORUM_ROLE, arr(uint256(_minAcceptQuorumPct), uint256(minAcceptQuorumPct))) { - require( - _minAcceptQuorumPct <= supportRequiredPct, - ERROR_CHANGE_QUORUM_PCTS - ); + require(_minAcceptQuorumPct <= supportRequiredPct, ERROR_CHANGE_QUORUM_PCTS); minAcceptQuorumPct = _minAcceptQuorumPct; emit ChangeMinQuorum(_minAcceptQuorumPct); } /** - * @notice Change vote time to `_voteTime` sec. The change affects all existing unexecuted votes, so be really careful with it - * @param _voteTime New vote time - */ - function unsafelyChangeVoteTime( - uint64 _voteTime - ) external auth(UNSAFELY_MODIFY_VOTE_TIME_ROLE) { + * @notice Change vote time to `_voteTime` sec. The change affects all existing unexecuted votes, so be really careful with it + * @param _voteTime New vote time + */ + function unsafelyChangeVoteTime(uint64 _voteTime) + external + auth(UNSAFELY_MODIFY_VOTE_TIME_ROLE) + { require(_voteTime > objectionPhaseTime, ERROR_CHANGE_VOTE_TIME); voteTime = _voteTime; @@ -233,12 +174,13 @@ contract Voting is IForwarder, AragonApp { } /** - * @notice Change the objection phase duration to `_objectionPhaseTime` sec. The change affects all existing unexecuted votes, so be really careful with it - * @param _objectionPhaseTime New objection time - */ - function unsafelyChangeObjectionPhaseTime( - uint64 _objectionPhaseTime - ) external auth(UNSAFELY_MODIFY_VOTE_TIME_ROLE) { + * @notice Change the objection phase duration to `_objectionPhaseTime` sec. The change affects all existing unexecuted votes, so be really careful with it + * @param _objectionPhaseTime New objection time + */ + function unsafelyChangeObjectionPhaseTime(uint64 _objectionPhaseTime) + external + auth(UNSAFELY_MODIFY_VOTE_TIME_ROLE) + { require(voteTime > _objectionPhaseTime, ERROR_CHANGE_OBJECTION_TIME); objectionPhaseTime = _objectionPhaseTime; @@ -246,72 +188,61 @@ contract Voting is IForwarder, AragonApp { } /** - * @notice Create a new vote about "`_metadata`" - * @param _executionScript EVM script to be executed on approval - * @param _metadata Vote metadata - * @return voteId Id for newly created vote - */ - function newVote( - bytes _executionScript, - string _metadata - ) external auth(CREATE_VOTES_ROLE) returns (uint256 voteId) { + * @notice Create a new vote about "`_metadata`" + * @param _executionScript EVM script to be executed on approval + * @param _metadata Vote metadata + * @return voteId Id for newly created vote + */ + function newVote(bytes _executionScript, string _metadata) external auth(CREATE_VOTES_ROLE) returns (uint256 voteId) { return _newVote(_executionScript, _metadata); } /** - * @notice Create a new vote about "`_metadata`" - * @dev _executesIfDecided was deprecated to introduce a proper lock period between decision and execution. - * @param _executionScript EVM script to be executed on approval - * @param _metadata Vote metadata - * @param _castVote_deprecated Whether to also cast newly created vote - * @param _executesIfDecided_deprecated Whether to also immediately execute newly created vote if decided - * @return voteId id for newly created vote - */ - function newVote( - bytes _executionScript, - string _metadata, - bool _castVote_deprecated, - bool _executesIfDecided_deprecated - ) external auth(CREATE_VOTES_ROLE) returns (uint256 voteId) { + * @notice Create a new vote about "`_metadata`" + * @dev _executesIfDecided was deprecated to introduce a proper lock period between decision and execution. + * @param _executionScript EVM script to be executed on approval + * @param _metadata Vote metadata + * @param _castVote_deprecated Whether to also cast newly created vote + * @param _executesIfDecided_deprecated Whether to also immediately execute newly created vote if decided + * @return voteId id for newly created vote + */ + function newVote(bytes _executionScript, string _metadata, bool _castVote_deprecated, bool _executesIfDecided_deprecated) + external + auth(CREATE_VOTES_ROLE) + returns (uint256 voteId) + { return _newVote(_executionScript, _metadata); } /** - * @notice Vote `_supports ? 'yes' : 'no'` in vote #`_voteId`. During objection phase one can only vote 'no' - * @dev Initialization check is implicitly provided by `voteExists()` as new votes can only be - * created via `newVote(),` which requires initialization - * @dev _executesIfDecided was deprecated to introduce a proper lock period between decision and execution. - * @param _voteId Id for vote - * @param _supports Whether voter supports the vote - * @param _executesIfDecided_deprecated Whether the vote should execute its action if it becomes decided - */ - function vote( - uint256 _voteId, - bool _supports, - bool _executesIfDecided_deprecated - ) external voteExists(_voteId) { + * @notice Vote `_supports ? 'yes' : 'no'` in vote #`_voteId`. During objection phase one can only vote 'no' + * @dev Initialization check is implicitly provided by `voteExists()` as new votes can only be + * created via `newVote(),` which requires initialization + * @dev _executesIfDecided was deprecated to introduce a proper lock period between decision and execution. + * @param _voteId Id for vote + * @param _supports Whether voter supports the vote + * @param _executesIfDecided_deprecated Whether the vote should execute its action if it becomes decided + */ + function vote(uint256 _voteId, bool _supports, bool _executesIfDecided_deprecated) external voteExists(_voteId) { require(_canParticipateInVote(_voteId, _supports), ERROR_CAN_NOT_VOTE); - require( - _hasVotingPower(votes[_voteId], msg.sender), - ERROR_NO_VOTING_POWER - ); + require(_hasVotingPower(votes[_voteId], msg.sender), ERROR_NO_VOTING_POWER); _vote(_voteId, _supports, msg.sender, false); } /** - * @notice Execute vote #`_voteId` - * @dev Initialization check is implicitly provided by `voteExists()` as new votes can only be - * created via `newVote(),` which requires initialization - * @param _voteId Id for vote - */ + * @notice Execute vote #`_voteId` + * @dev Initialization check is implicitly provided by `voteExists()` as new votes can only be + * created via `newVote(),` which requires initialization + * @param _voteId Id for vote + */ function executeVote(uint256 _voteId) external voteExists(_voteId) { _executeVote(_voteId); } /** - * @notice Assign `_delegate` as the delegate for the sender - * @param _delegate address to delegate to - */ + * @notice Assign `_delegate` as the delegate for the sender + * @param _delegate address to delegate to + */ function setDelegate(address _delegate) public { require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); @@ -321,10 +252,7 @@ contract Voting is IForwarder, AragonApp { address prevDelegate = delegates[msgSender].delegate; require(_delegate != prevDelegate, ERROR_DELEGATE_SAME_AS_PREV); - uint256 votingPower = token.balanceOfAt( - msgSender, - getBlockNumber64() - 1 - ); + uint256 votingPower = token.balanceOfAt(msgSender, getBlockNumber64() - 1); require(votingPower > 0, ERROR_NO_VOTING_POWER); if (prevDelegate != address(0)) { @@ -352,11 +280,7 @@ contract Voting is IForwarder, AragonApp { * @param _supports Whether the delegate supports the vote * @param _voters list of voters */ - function voteForMultiple( - uint256 _voteId, - bool _supports, - address[] _voters - ) public voteExists(_voteId) { + function voteForMultiple(uint256 _voteId, bool _supports, address[] _voters) public voteExists(_voteId) { require(_canParticipateInVote(_voteId, _supports), ERROR_CAN_NOT_VOTE); Vote storage vote_ = votes[_voteId]; @@ -365,10 +289,7 @@ contract Voting is IForwarder, AragonApp { for (uint256 i = 0; i < _voters.length; ++i) { address voter = _voters[i]; require(_hasVotingPower(vote_, voter), ERROR_NO_VOTING_POWER); - require( - _canVoteFor(vote_, msgSender, voter), - ERROR_CAN_NOT_VOTE_FOR - ); + require(_canVoteFor(vote_, msgSender, voter), ERROR_CAN_NOT_VOTE_FOR); _vote(_voteId, _supports, voter, true); } } @@ -379,11 +300,7 @@ contract Voting is IForwarder, AragonApp { * @param _supports Whether the delegate supports the vote * @param _voter address of the voter */ - function voteFor( - uint256 _voteId, - bool _supports, - address _voter - ) external voteExists(_voteId) { + function voteFor(uint256 _voteId, bool _supports, address _voter) external voteExists(_voteId) { address[] memory voters = new address[](1); voters[0] = _voter; voteForMultiple(_voteId, _supports, voters); @@ -392,30 +309,30 @@ contract Voting is IForwarder, AragonApp { // Forwarding fns /** - * @notice Tells whether the Voting app is a forwarder or not - * @dev IForwarder interface conformance - * @return Always true - */ + * @notice Tells whether the Voting app is a forwarder or not + * @dev IForwarder interface conformance + * @return Always true + */ function isForwarder() external pure returns (bool) { return true; } /** - * @notice Creates a vote to execute the desired action - * @dev IForwarder interface conformance - * @param _evmScript Start vote with script - */ + * @notice Creates a vote to execute the desired action + * @dev IForwarder interface conformance + * @param _evmScript Start vote with script + */ function forward(bytes _evmScript) public { require(canForward(msg.sender, _evmScript), ERROR_CAN_NOT_FORWARD); _newVote(_evmScript, ""); } /** - * @notice Tells whether `_sender` can forward actions or not - * @dev IForwarder interface conformance - * @param _sender Address of the account intending to forward an action - * @return True if the given address can create votes, false otherwise - */ + * @notice Tells whether `_sender` can forward actions or not + * @dev IForwarder interface conformance + * @param _sender Address of the account intending to forward an action + * @return True if the given address can create votes, false otherwise + */ function canForward(address _sender, bytes) public view returns (bool) { // Note that `canPerform()` implicitly does an initialization check itself return canPerform(_sender, CREATE_VOTES_ROLE, arr()); @@ -424,67 +341,56 @@ contract Voting is IForwarder, AragonApp { // Getter fns /** - * @notice Tells whether a vote #`_voteId` can be executed or not - * @dev Initialization check is implicitly provided by `voteExists()` as new votes can only be - * created via `newVote(),` which requires initialization - * @param _voteId Vote identifier - * @return True if the given vote can be executed, false otherwise - */ - function canExecute( - uint256 _voteId - ) public view voteExists(_voteId) returns (bool) { + * @notice Tells whether a vote #`_voteId` can be executed or not + * @dev Initialization check is implicitly provided by `voteExists()` as new votes can only be + * created via `newVote(),` which requires initialization + * @param _voteId Vote identifier + * @return True if the given vote can be executed, false otherwise + */ + function canExecute(uint256 _voteId) public view voteExists(_voteId) returns (bool) { return _canExecute(_voteId); } /** - * @notice Tells whether `_voter` can participate in the main or objection phase of the vote #`_voteId` - * @dev Initialization check is implicitly provided by `voteExists()` as new votes can only be - * created via `newVote(),` which requires initialization - * @param _voteId Vote identifier - * @param _voter address of the voter to check - * @return True if the given voter can participate in the main phase of a certain vote, false otherwise - */ - function canVote( - uint256 _voteId, - address _voter - ) external view voteExists(_voteId) returns (bool) { + * @notice Tells whether `_voter` can participate in the main or objection phase of the vote #`_voteId` + * @dev Initialization check is implicitly provided by `voteExists()` as new votes can only be + * created via `newVote(),` which requires initialization + * @param _voteId Vote identifier + * @param _voter address of the voter to check + * @return True if the given voter can participate in the main phase of a certain vote, false otherwise + */ + function canVote(uint256 _voteId, address _voter) external view voteExists(_voteId) returns (bool) { Vote storage vote_ = votes[_voteId]; - return - _canParticipateInVote(_voteId, false) && - _hasVotingPower(vote_, _voter); + return _canParticipateInVote(_voteId, false) && _hasVotingPower(vote_, _voter); } /** - * @notice Tells the current phase of the vote #`_voteId` - * @dev Initialization check is implicitly provided by `voteExists()` as new votes can only be - * created via `newVote(),` which requires initialization - * @param _voteId Vote identifier - * @return VotePhase.Main if one can vote yes or no and VotePhase.Objection if one can vote only no and VotingPhase.Closed if no votes are accepted - */ - function getVotePhase( - uint256 _voteId - ) external view voteExists(_voteId) returns (VotePhase) { + * @notice Tells the current phase of the vote #`_voteId` + * @dev Initialization check is implicitly provided by `voteExists()` as new votes can only be + * created via `newVote(),` which requires initialization + * @param _voteId Vote identifier + * @return VotePhase.Main if one can vote yes or no and VotePhase.Objection if one can vote only no and VotingPhase.Closed if no votes are accepted + */ + function getVotePhase(uint256 _voteId) external view voteExists(_voteId) returns (VotePhase) { return _getVotePhase(votes[_voteId]); } /** - * @dev Return all information for a vote by its ID - * @param _voteId Vote identifier - * @return true if the vote is open - * @return Vote executed status - * @return Vote start date - * @return Vote snapshot block - * @return Vote support required - * @return Vote minimum acceptance quorum - * @return Vote yeas amount - * @return Vote nays amount - * @return Vote power - * @return Vote script - * @return Vote phase - */ - function getVote( - uint256 _voteId - ) + * @dev Return all information for a vote by its ID + * @param _voteId Vote identifier + * @return true if the vote is open + * @return Vote executed status + * @return Vote start date + * @return Vote snapshot block + * @return Vote support required + * @return Vote minimum acceptance quorum + * @return Vote yeas amount + * @return Vote nays amount + * @return Vote power + * @return Vote script + * @return Vote phase + */ + function getVote(uint256 _voteId) public view voteExists(_voteId) @@ -518,15 +424,12 @@ contract Voting is IForwarder, AragonApp { } /** - * @dev Return the state of a voter for a given vote by its ID - * @param _voteId Vote identifier - * @param _voter address of the voter - * @return VoterState of the requested voter for a certain vote - */ - function getVoterState( - uint256 _voteId, - address _voter - ) public view voteExists(_voteId) returns (VoterState) { + * @dev Return the state of a voter for a given vote by its ID + * @param _voteId Vote identifier + * @param _voter address of the voter + * @return VoterState of the requested voter for a certain vote + */ + function getVoterState(uint256 _voteId, address _voter) public view voteExists(_voteId) returns (VoterState) { return votes[_voteId].voters[_voter]; } @@ -538,18 +441,8 @@ contract Voting is IForwarder, AragonApp { * @return the array of delegated voters * @return the array of voting power of delegated voters */ - function getDelegatedVoters( - address _delegate, - uint256 _offset, - uint256 _limit - ) public view returns (address[] memory, uint256[] memory) { - return - _getDelegatedVotersAt( - _delegate, - _offset, - _limit, - getBlockNumber64() - ); + function getDelegatedVoters(address _delegate, uint256 _offset, uint256 _limit) public view returns (address[] memory, uint256[] memory) { + return _getDelegatedVotersAt(_delegate, _offset, _limit, getBlockNumber64()); } /** @@ -560,35 +453,17 @@ contract Voting is IForwarder, AragonApp { * @return the array of delegated voters * @return the array of voting power of delegated voters */ - function getDelegatedVotersAtVote( - address _delegate, - uint256 _offset, - uint256 _limit, - uint256 _voteId - ) - public - view - voteExists(_voteId) - returns (address[] memory, uint256[] memory) - { + function getDelegatedVotersAtVote(address _delegate, uint256 _offset, uint256 _limit, uint256 _voteId) public view voteExists(_voteId) returns (address[] memory, uint256[] memory) { Vote storage vote_ = votes[_voteId]; - return - _getDelegatedVotersAt( - _delegate, - _offset, - _limit, - vote_.snapshotBlock - ); + return _getDelegatedVotersAt(_delegate, _offset, _limit, vote_.snapshotBlock); } /** - * @dev Return the number of delegated voters for the `_delegate` - * @param _delegate address of the delegate - * @return the number of delegated voters - */ - function getDelegatedVotersCount( - address _delegate - ) public view returns (uint256) { + * @dev Return the number of delegated voters for the `_delegate` + * @param _delegate address of the delegate + * @return the number of delegated voters + */ + function getDelegatedVotersCount(address _delegate) public view returns (uint256) { require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); return delegatedVoters[_delegate].addresses.length; } @@ -607,15 +482,7 @@ contract Voting is IForwarder, AragonApp { * @param _voters the list of voters * @param _voteId Vote identifier */ - function getVotersStateAtVote( - address[] _voters, - uint256 _voteId - ) - public - view - voteExists(_voteId) - returns (VoterState[] memory voterStatesList) - { + function getVotersStateAtVote(address[] _voters, uint256 _voteId) public view voteExists(_voteId) returns (VoterState[] memory voterStatesList) { uint256 length = _voters.length; voterStatesList = new VoterState[](length); Vote storage vote_ = votes[_voteId]; @@ -628,13 +495,10 @@ contract Voting is IForwarder, AragonApp { // Internal fns /** - * @dev Internal function to create a new vote - * @return voteId id for newly created vote - */ - function _newVote( - bytes _executionScript, - string _metadata - ) internal returns (uint256 voteId) { + * @dev Internal function to create a new vote + * @return voteId id for newly created vote + */ + function _newVote(bytes _executionScript, string _metadata) internal returns (uint256 voteId) { uint64 snapshotBlock = getBlockNumber64() - 1; // avoid double voting in this very block uint256 votingPower = token.totalSupplyAt(snapshotBlock); require(votingPower > 0, ERROR_NO_VOTING_POWER); @@ -656,12 +520,7 @@ contract Voting is IForwarder, AragonApp { * @dev Internal function to cast a vote or object to. @dev It assumes that voter can support or object to the vote */ - function _vote( - uint256 _voteId, - bool _supports, - address _voter, - bool _isDelegate - ) internal { + function _vote(uint256 _voteId, bool _supports, address _voter, bool _isDelegate) internal { Vote storage vote_ = votes[_voteId]; // This could re-enter, though we can assume the governance token is not malicious @@ -677,14 +536,10 @@ contract Voting is IForwarder, AragonApp { if (_supports) { vote_.yea = vote_.yea.add(voterStake); - vote_.voters[_voter] = _isDelegate - ? VoterState.DelegateYea - : VoterState.Yea; + vote_.voters[_voter] = _isDelegate ? VoterState.DelegateYea : VoterState.Yea; } else { vote_.nay = vote_.nay.add(voterStake); - vote_.voters[_voter] = _isDelegate - ? VoterState.DelegateNay - : VoterState.Nay; + vote_.voters[_voter] = _isDelegate ? VoterState.DelegateNay : VoterState.Nay; } emit CastVote(_voteId, _voter, _supports, voterStake); @@ -694,27 +549,21 @@ contract Voting is IForwarder, AragonApp { } if (_isDelegate) { - emit CastVoteAsDelegate( - _voteId, - msg.sender, - _voter, - _supports, - voterStake - ); + emit CastVoteAsDelegate(_voteId, msg.sender, _voter, _supports, voterStake); } } /** - * @dev Internal function to execute a vote. It assumes the queried vote exists. - */ + * @dev Internal function to execute a vote. It assumes the queried vote exists. + */ function _executeVote(uint256 _voteId) internal { require(_canExecute(_voteId), ERROR_CAN_NOT_EXECUTE); _unsafeExecuteVote(_voteId); } /** - * @dev Unsafe version of _executeVote that assumes you have already checked if the vote can be executed and exists - */ + * @dev Unsafe version of _executeVote that assumes you have already checked if the vote can be executed and exists + */ function _unsafeExecuteVote(uint256 _voteId) internal { Vote storage vote_ = votes[_voteId]; @@ -731,18 +580,12 @@ contract Voting is IForwarder, AragonApp { * @param _delegate address of the delegate * @param _voter address of the voter */ - function _addDelegatedAddressFor( - address _delegate, - address _voter - ) internal { + function _addDelegatedAddressFor(address _delegate, address _voter) internal { uint256 length = delegatedVoters[_delegate].addresses.length; require(length < UINT_96_MAX, ERROR_MAX_DELEGATED_VOTERS_REACHED); delegatedVoters[_delegate].addresses.push(_voter); - delegates[_voter] = Delegate( - _delegate, - uint96(delegatedVoters[_delegate].addresses.length.sub(1)) - ); + delegates[_voter] = Delegate(_delegate, uint96(delegatedVoters[_delegate].addresses.length.sub(1))); } /** @@ -750,10 +593,7 @@ contract Voting is IForwarder, AragonApp { * @param _delegate address of the delegate * @param _voter address of the voter */ - function _removeDelegatedAddressFor( - address _delegate, - address _voter - ) internal { + function _removeDelegatedAddressFor(address _delegate, address _voter) internal { uint256 length = delegatedVoters[_delegate].addresses.length; require(length > 0, ERROR_DELEGATE_NOT_SET); @@ -774,16 +614,7 @@ contract Voting is IForwarder, AragonApp { * @return the array of delegated voters * @return the array of voting power of delegated voters */ - function _getDelegatedVotersAt( - address _delegate, - uint256 _offset, - uint256 _limit, - uint256 _blockNumber - ) - internal - view - returns (address[] memory votersList, uint256[] memory votingPowerList) - { + function _getDelegatedVotersAt(address _delegate, uint256 _offset, uint256 _limit, uint256 _blockNumber) internal view returns (address[] memory votersList, uint256[] memory votingPowerList) { require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); require(_limit > 0, ERROR_INVALID_LIMIT); uint256 length = delegatedVoters[_delegate].addresses.length; @@ -792,9 +623,7 @@ contract Voting is IForwarder, AragonApp { } require(_offset < length, ERROR_INVALID_OFFSET); - uint256 returnCount = _offset.add(_limit) > length - ? length.sub(_offset) - : _limit; + uint256 returnCount = _offset.add(_limit) > length ? length.sub(_offset) : _limit; votersList = new address[](returnCount); votingPowerList = new uint256[](returnCount); for (uint256 i = 0; i < returnCount; ++i) { @@ -806,9 +635,9 @@ contract Voting is IForwarder, AragonApp { } /** - * @dev Internal function to check if a vote can be executed. It assumes the queried vote exists. - * @return True if the given vote can be executed, false otherwise - */ + * @dev Internal function to check if a vote can be executed. It assumes the queried vote exists. + * @return True if the given vote can be executed, false otherwise + */ function _canExecute(uint256 _voteId) internal view returns (bool) { Vote storage vote_ = votes[_voteId]; @@ -828,9 +657,7 @@ contract Voting is IForwarder, AragonApp { return false; } // Has min quorum? - if ( - !_isValuePct(voteYea, vote_.votingPower, vote_.minAcceptQuorumPct) - ) { + if (!_isValuePct(voteYea, vote_.votingPower, vote_.minAcceptQuorumPct)) { return false; } @@ -838,64 +665,43 @@ contract Voting is IForwarder, AragonApp { } /** - * @dev Internal function to check if a voter can participate on a vote. It assumes the queried vote exists. - * @return True if the given voter can participate a certain vote, false otherwise - */ - function _canParticipateInVote( - uint256 _voteId, - bool _supports - ) internal view returns (bool) { + * @dev Internal function to check if a voter can participate on a vote. It assumes the queried vote exists. + * @return True if the given voter can participate a certain vote, false otherwise + */ + function _canParticipateInVote(uint256 _voteId, bool _supports) internal view returns (bool) { Vote storage vote_ = votes[_voteId]; return _isVoteOpen(vote_) && _isValidPhaseToVote(vote_, _supports); } - function _isDelegateFor( - address _delegate, - address _voter - ) internal view returns (bool) { + function _isDelegateFor(address _delegate, address _voter) internal view returns (bool) { if (_delegate == address(0) || _voter == address(0)) { return false; } return delegates[_voter].delegate == _delegate; } - function _canVoteFor( - Vote storage _vote, - address _delegate, - address _voter - ) internal view returns (bool) { + function _canVoteFor(Vote storage _vote, address _delegate, address _voter) internal view returns (bool) { return _isDelegateFor(_delegate, _voter) && !_hasVoted(_vote, _voter); } - function _isValidPhaseToVote( - Vote storage _vote, - bool _supports - ) internal view returns (bool) { + function _isValidPhaseToVote(Vote storage _vote, bool _supports) internal view returns (bool) { return !_supports || _getVotePhase(_vote) == VotePhase.Main; } - function _hasVoted( - Vote storage _vote, - address _voter - ) internal view returns (bool) { + function _hasVoted(Vote storage _vote, address _voter) internal view returns (bool) { VoterState state = _vote.voters[_voter]; return state == VoterState.Yea || state == VoterState.Nay; } - function _hasVotingPower( - Vote storage _vote, - address _voter - ) internal view returns (bool) { + function _hasVotingPower(Vote storage _vote, address _voter) internal view returns (bool) { return token.balanceOfAt(_voter, _vote.snapshotBlock) > 0; } /** - * @dev Internal function to get the current phase of the vote. It assumes the queried vote exists. - * @return VotePhase.Main if one can vote 'yes' or 'no', VotePhase.Objection if one can vote only 'no' or VotePhase.Closed if no votes are accepted - */ - function _getVotePhase( - Vote storage vote_ - ) internal view returns (VotePhase) { + * @dev Internal function to get the current phase of the vote. It assumes the queried vote exists. + * @return VotePhase.Main if one can vote 'yes' or 'no', VotePhase.Objection if one can vote only 'no' or VotePhase.Closed if no votes are accepted + */ + function _getVotePhase(Vote storage vote_) internal view returns (VotePhase) { uint64 timestamp = getTimestamp64(); uint64 voteTimeEnd = vote_.startDate.add(voteTime); if (timestamp < voteTimeEnd.sub(objectionPhaseTime)) { @@ -908,22 +714,17 @@ contract Voting is IForwarder, AragonApp { } /** - * @dev Internal function to check if a vote is still open for both support and objection - * @return True if less than voteTime has passed since the vote start - */ + * @dev Internal function to check if a vote is still open for both support and objection + * @return True if less than voteTime has passed since the vote start + */ function _isVoteOpen(Vote storage vote_) internal view returns (bool) { - return - getTimestamp64() < vote_.startDate.add(voteTime) && !vote_.executed; + return getTimestamp64() < vote_.startDate.add(voteTime) && !vote_.executed; } /** - * @dev Calculates whether `_value` is more than a percentage `_pct` of `_total` - */ - function _isValuePct( - uint256 _value, - uint256 _total, - uint256 _pct - ) internal pure returns (bool) { + * @dev Calculates whether `_value` is more than a percentage `_pct` of `_total` + */ + function _isValuePct(uint256 _value, uint256 _total, uint256 _pct) internal pure returns (bool) { if (_total == 0) { return false; } diff --git a/apps/voting/test/voting.js b/apps/voting/test/voting.js index cbca5716c..92d791635 100644 --- a/apps/voting/test/voting.js +++ b/apps/voting/test/voting.js @@ -1,1789 +1,987 @@ -const ERRORS = require("./helpers/errors"); -const assertArraysEqualAsSets = require("./helpers/assertArrayAsSets"); -const { - assertBn, - assertRevert, - assertAmountOfEvents, - assertEvent, -} = require("@aragon/contract-helpers-test/src/asserts"); -const { - pct16, - bn, - bigExp, - getEventArgument, - ZERO_ADDRESS, -} = require("@aragon/contract-helpers-test"); -const { - newDao, - installNewApp, - encodeCallScript, - ANY_ENTITY, - EMPTY_CALLS_SCRIPT, -} = require("@aragon/contract-helpers-test/src/aragon-os"); -const { assert } = require("chai"); - -const Voting = artifacts.require("VotingMock"); - -const MiniMeToken = artifacts.require("MiniMeToken"); -const ExecutionTarget = artifacts.require("ExecutionTarget"); - -const createdVoteId = (receipt) => - getEventArgument(receipt, "StartVote", "voteId"); - -const VOTER_STATE = [ - "ABSENT", - "YEA", - "NAY", - "DELEGATE_YEA", - "DELEGATE_NAY", -].reduce((state, key, index) => { +const ERRORS = require('./helpers/errors') +const assertArraysEqualAsSets = require('./helpers/assertArrayAsSets') +const { assertBn, assertRevert, assertAmountOfEvents, assertEvent } = require('@aragon/contract-helpers-test/src/asserts') +const { pct16, bn, bigExp, getEventArgument, ZERO_ADDRESS } = require('@aragon/contract-helpers-test') +const { newDao, installNewApp, encodeCallScript, ANY_ENTITY, EMPTY_CALLS_SCRIPT } = require('@aragon/contract-helpers-test/src/aragon-os') +const { assert } = require('chai') + +const Voting = artifacts.require('VotingMock') + +const MiniMeToken = artifacts.require('MiniMeToken') +const ExecutionTarget = artifacts.require('ExecutionTarget') + +const createdVoteId = receipt => getEventArgument(receipt, 'StartVote', 'voteId') + +const VOTER_STATE = ['ABSENT', 'YEA', 'NAY', 'DELEGATE_YEA', 'DELEGATE_NAY'].reduce((state, key, index) => { state[key] = index; return state; -}, {}); - -contract( - "Voting App", - ([ - root, - holder1, - holder2, - holder20, - holder29, - holder51, - delegate1, - delegate2, - nonHolder, - ]) => { - let votingBase, voting, token, executionTarget, aclP; - let CREATE_VOTES_ROLE, - MODIFY_SUPPORT_ROLE, - MODIFY_QUORUM_ROLE, - UNSAFELY_MODIFY_VOTE_TIME_ROLE; - - const NOW = 1; - const mainPhase = 700; - const objectionPhase = 300; - const votingDuration = mainPhase + objectionPhase; - - const APP_ID = - "0x1234123412341234123412341234123412341234123412341234123412341234"; - - before("load roles", async () => { - votingBase = await Voting.new(); - CREATE_VOTES_ROLE = await votingBase.CREATE_VOTES_ROLE(); - MODIFY_SUPPORT_ROLE = await votingBase.MODIFY_SUPPORT_ROLE(); - MODIFY_QUORUM_ROLE = await votingBase.MODIFY_QUORUM_ROLE(); - UNSAFELY_MODIFY_VOTE_TIME_ROLE = - await votingBase.UNSAFELY_MODIFY_VOTE_TIME_ROLE(); - }); - - beforeEach("deploy DAO with Voting app", async () => { - const { dao, acl } = await newDao(root); - voting = await Voting.at( - await installNewApp(dao, APP_ID, votingBase.address, root) - ); - await voting.mockSetTimestamp(NOW); - await acl.createPermission( - ANY_ENTITY, - voting.address, - CREATE_VOTES_ROLE, - root, - { from: root } - ); - await acl.createPermission( - ANY_ENTITY, - voting.address, - MODIFY_SUPPORT_ROLE, - root, - { from: root } - ); - await acl.createPermission( - ANY_ENTITY, - voting.address, - MODIFY_QUORUM_ROLE, - root, - { from: root } - ); - await acl.createPermission( - ANY_ENTITY, - voting.address, - UNSAFELY_MODIFY_VOTE_TIME_ROLE, - root, - { from: root } - ); - aclP = acl; - }); - - context("normal token supply, common tests", () => { - const neededSupport = pct16(50); - const minimumAcceptanceQuorum = pct16(20); +}, {}) + +contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, delegate1, delegate2, nonHolder]) => { + let votingBase, voting, token, executionTarget, aclP + let CREATE_VOTES_ROLE, MODIFY_SUPPORT_ROLE, MODIFY_QUORUM_ROLE, UNSAFELY_MODIFY_VOTE_TIME_ROLE + + const NOW = 1 + const mainPhase = 700 + const objectionPhase = 300 + const votingDuration = mainPhase + objectionPhase + + const APP_ID = '0x1234123412341234123412341234123412341234123412341234123412341234' + + before('load roles', async () => { + votingBase = await Voting.new() + CREATE_VOTES_ROLE = await votingBase.CREATE_VOTES_ROLE() + MODIFY_SUPPORT_ROLE = await votingBase.MODIFY_SUPPORT_ROLE() + MODIFY_QUORUM_ROLE = await votingBase.MODIFY_QUORUM_ROLE() + UNSAFELY_MODIFY_VOTE_TIME_ROLE = await votingBase.UNSAFELY_MODIFY_VOTE_TIME_ROLE() + }) + + beforeEach('deploy DAO with Voting app', async () => { + const { dao, acl } = await newDao(root) + voting = await Voting.at(await installNewApp(dao, APP_ID, votingBase.address, root)) + await voting.mockSetTimestamp(NOW) + await acl.createPermission(ANY_ENTITY, voting.address, CREATE_VOTES_ROLE, root, { from: root }) + await acl.createPermission(ANY_ENTITY, voting.address, MODIFY_SUPPORT_ROLE, root, { from: root }) + await acl.createPermission(ANY_ENTITY, voting.address, MODIFY_QUORUM_ROLE, root, { from: root }) + await acl.createPermission(ANY_ENTITY, voting.address, UNSAFELY_MODIFY_VOTE_TIME_ROLE, root, { from: root }) + aclP = acl + }) + + context('normal token supply, common tests', () => { + const neededSupport = pct16(50) + const minimumAcceptanceQuorum = pct16(20) + + beforeEach(async () => { + token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', 0, 'n', true) // empty parameters minime + await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, objectionPhase) + executionTarget = await ExecutionTarget.new() + }) + + it('fails on reinitialization', async () => { + await assertRevert(voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, + objectionPhase), ERRORS.INIT_ALREADY_INITIALIZED) + }) + + it('cannot initialize base app', async () => { + const newVoting = await Voting.new() + assert.isTrue(await newVoting.isPetrified()) + await assertRevert(newVoting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, + objectionPhase), ERRORS.INIT_ALREADY_INITIALIZED) + }) + + it('checks it is forwarder', async () => { + assert.isTrue(await voting.isForwarder()) + }) + + it('can change required support', async () => { + const receipt = await voting.changeSupportRequiredPct(neededSupport.add(bn(1))) + assertAmountOfEvents(receipt, 'ChangeSupportRequired') + + assertBn(await voting.supportRequiredPct(), neededSupport.add(bn(1)), 'should have changed required support') + }) + + it('fails changing required support lower than minimum acceptance quorum', async () => { + await assertRevert(voting.changeSupportRequiredPct(minimumAcceptanceQuorum.sub(bn(1))), ERRORS.VOTING_CHANGE_SUPPORT_PCTS) + }) + + it('fails changing required support to 100% or more', async () => { + await assertRevert(voting.changeSupportRequiredPct(pct16(101)), ERRORS.VOTING_CHANGE_SUPP_TOO_BIG) + await assertRevert(voting.changeSupportRequiredPct(pct16(100)), ERRORS.VOTING_CHANGE_SUPP_TOO_BIG) + }) + + it('can change minimum acceptance quorum', async () => { + const receipt = await voting.changeMinAcceptQuorumPct(1) + assertAmountOfEvents(receipt, 'ChangeMinQuorum') + + assert.equal(await voting.minAcceptQuorumPct(), 1, 'should have changed acceptance quorum') + }) + + it('fails changing minimum acceptance quorum to greater than min support', async () => { + await assertRevert(voting.changeMinAcceptQuorumPct(neededSupport.add(bn(1))), ERRORS.VOTING_CHANGE_QUORUM_PCTS) + }) + + }) + + for (const decimals of [0, 2, 18, 26]) { + context(`normal token supply, ${decimals} decimals`, () => { + const neededSupport = pct16(50) + const minimumAcceptanceQuorum = pct16(20) beforeEach(async () => { - token = await MiniMeToken.new( - ZERO_ADDRESS, - ZERO_ADDRESS, - 0, - "n", - 0, - "n", - true - ); // empty parameters minime - await voting.initialize( - token.address, - neededSupport, - minimumAcceptanceQuorum, - votingDuration, - objectionPhase - ); - executionTarget = await ExecutionTarget.new(); - }); + token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', decimals, 'n', true) // empty parameters minime + + await token.generateTokens(holder20, bigExp(20, decimals)) + await token.generateTokens(holder29, bigExp(29, decimals)) + await token.generateTokens(holder51, bigExp(51, decimals)) + + await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, objectionPhase) + + executionTarget = await ExecutionTarget.new() + }) + + it('execution scripts can execute multiple actions', async () => { + const action = { to: executionTarget.address, calldata: executionTarget.contract.methods.execute().encodeABI() } + const script = encodeCallScript([action, action, action]) + + const voteId = createdVoteId(await voting.newVote(script, '', { from: holder51 })) + await voting.vote(voteId, true, true, { from: holder51 }) + await voting.mockIncreaseTime(votingDuration + 1) + await voting.executeVote(voteId) + + assert.equal(await executionTarget.counter(), 3, 'should have executed multiple times') + }) + + it('execution script can be empty', async () => { + const voteId = createdVoteId(await voting.newVote(encodeCallScript([]), '', { from: holder51 })) + await voting.vote(voteId, true, true, { from: holder51 }) + await voting.mockIncreaseTime(votingDuration + 1) + await voting.executeVote(voteId) + }) + + it('check castVote do nothing (deprecated)', async () => { + let voteId = createdVoteId(await voting.methods['newVote(bytes,string,bool,bool)'](encodeCallScript([]), '', true, false, { from: holder51 })) + assert.equal(await voting.getVoterState(voteId, holder51), VOTER_STATE.ABSENT, 'holder51 should not have voted') + }) + + it('check executesIfDecided do nothing (deprecated)', async () => { + let voteId = createdVoteId(await voting.methods['newVote(bytes,string,bool,bool)'](encodeCallScript([]), '', true, false, { from: holder51 })) + await voting.vote(voteId, true, true, { from: holder51 }) + assert.equal(await voting.canExecute(voteId), false, 'should be in the unexecuted state') + await voting.mockIncreaseTime(votingDuration + 1) + await voting.executeVote(voteId) + assert.equal(await voting.canExecute(voteId), false, 'should be in the executed state') + + voteId = createdVoteId(await voting.methods['newVote(bytes,string,bool,bool)'](encodeCallScript([]), '', true, true, { from: holder51 })) + await voting.vote(voteId, true, true, { from: holder51 }) + assert.equal(await voting.canExecute(voteId), false, 'should be in the unexecuted state') + await voting.mockIncreaseTime(votingDuration + 1) + await voting.executeVote(voteId) + assert.equal(await voting.canExecute(voteId), false, 'should be in the executed state') + }) + + it('execution throws if any action on script throws', async () => { + const action = { to: executionTarget.address, calldata: executionTarget.contract.methods.execute().encodeABI() } + let script = encodeCallScript([action]) + script = script.slice(0, -2) // remove one byte from calldata for it to fail + + const voteId = createdVoteId(await voting.newVote(script, '', { from: holder51 })) + await voting.mockIncreaseTime(votingDuration + 1) + await assertRevert(voting.executeVote(voteId)) + }) + + it('forwarding creates vote', async () => { + const action = { to: executionTarget.address, calldata: executionTarget.contract.methods.execute().encodeABI() } + const script = encodeCallScript([action]) + const voteId = createdVoteId(await voting.forward(script, { from: holder51 })) + assert.equal(voteId, 0, 'voting should have been created') + }) + + context('creating vote', () => { + let script, voteId, creator, metadata - it("fails on reinitialization", async () => { - await assertRevert( - voting.initialize( - token.address, - neededSupport, - minimumAcceptanceQuorum, - votingDuration, - objectionPhase - ), - ERRORS.INIT_ALREADY_INITIALIZED - ); - }); - - it("cannot initialize base app", async () => { - const newVoting = await Voting.new(); - assert.isTrue(await newVoting.isPetrified()); - await assertRevert( - newVoting.initialize( - token.address, - neededSupport, - minimumAcceptanceQuorum, - votingDuration, - objectionPhase - ), - ERRORS.INIT_ALREADY_INITIALIZED - ); - }); - - it("checks it is forwarder", async () => { - assert.isTrue(await voting.isForwarder()); - }); - - it("can change required support", async () => { - const receipt = await voting.changeSupportRequiredPct( - neededSupport.add(bn(1)) - ); - assertAmountOfEvents(receipt, "ChangeSupportRequired"); - - assertBn( - await voting.supportRequiredPct(), - neededSupport.add(bn(1)), - "should have changed required support" - ); - }); - - it("fails changing required support lower than minimum acceptance quorum", async () => { - await assertRevert( - voting.changeSupportRequiredPct(minimumAcceptanceQuorum.sub(bn(1))), - ERRORS.VOTING_CHANGE_SUPPORT_PCTS - ); - }); + beforeEach(async () => { + const action = { to: executionTarget.address, calldata: executionTarget.contract.methods.execute().encodeABI() } + script = encodeCallScript([action, action]) + + const receipt = await voting.methods['newVote(bytes,string,bool,bool)'](script, 'metadata', false, false, { from: holder51 }); + voteId = getEventArgument(receipt, 'StartVote', 'voteId') + creator = getEventArgument(receipt, 'StartVote', 'creator') + metadata = getEventArgument(receipt, 'StartVote', 'metadata') + }) + + it('has correct state', async () => { + const { open, executed, snapshotBlock, supportRequired, minAcceptQuorum, yea, nay, votingPower, script: execScript } = await voting.getVote(voteId) + assert.isTrue(open, 'vote should be open') + assert.isFalse(executed, 'vote should not be executed') + assert.equal(creator, holder51, 'creator should be correct') + assertBn(snapshotBlock, await web3.eth.getBlockNumber() - 1, 'snapshot block should be correct') + assertBn(supportRequired, neededSupport, 'required support should be app required support') + assertBn(minAcceptQuorum, minimumAcceptanceQuorum, 'min quorum should be app min quorum') + assertBn(yea, 0, 'initial yea should be 0') + assertBn(nay, 0, 'initial nay should be 0') + assertBn(votingPower, bigExp(100, decimals), 'voting power should be 100') + assert.equal(execScript, script, 'script should be correct') + assert.equal(metadata, 'metadata', 'should have returned correct metadata') + assert.equal(await voting.getVoterState(voteId, nonHolder), VOTER_STATE.ABSENT, 'nonHolder should not have voted') + }) + + it('fails getting a vote out of bounds', async () => { + await assertRevert(voting.getVote(voteId + 1), ERRORS.VOTING_NO_VOTE) + }) + + it('changing required support does not affect vote required support', async () => { + await voting.changeSupportRequiredPct(pct16(70)) + + // With previous required support at 50%, vote should be approved + // with new quorum at 70% it shouldn't have, but since min quorum is snapshotted + // it will succeed + + await voting.vote(voteId, true, false, { from: holder51 }) + await voting.vote(voteId, true, false, { from: holder20 }) + await voting.vote(voteId, false, false, { from: holder29 }) + await voting.mockIncreaseTime(votingDuration + 1) + + const state = await voting.getVote(voteId) + assertBn(state[4], neededSupport, 'required support in vote should stay equal') + await voting.executeVote(voteId) // exec doesn't fail + }) + + it('changing min quorum doesnt affect vote min quorum', async () => { + await voting.changeMinAcceptQuorumPct(pct16(50)) + + // With previous min acceptance quorum at 20%, vote should be approved + // with new quorum at 50% it shouldn't have, but since min quorum is snapshotted + // it will succeed + + const tx = await voting.vote(voteId, true, true, { from: holder29 }) + assertEvent(tx, 'CastVote', { expectedArgs: { voteId: voteId, voter: holder29, supports: true }}) + assertAmountOfEvents(tx, 'CastVote', { expectedAmount: 1 }) + assertAmountOfEvents(tx, 'CastObjection', { expectedAmount: 0 }) + + await voting.mockIncreaseTime(votingDuration + 1) + + const state = await voting.getVote(voteId) + assertBn(state[5], minimumAcceptanceQuorum, 'acceptance quorum in vote should stay equal') + await voting.executeVote(voteId) // exec doesn't fail + }) + + it('holder can vote', async () => { + const tx = await voting.vote(voteId, false, true, { from: holder29 }) + assertEvent(tx, 'CastVote', { expectedArgs: { voteId: voteId, voter: holder29, supports: false }}) + assertAmountOfEvents(tx, 'CastVote', { expectedAmount: 1 }) + assertAmountOfEvents(tx, 'CastObjection', { expectedAmount: 0 }) + + const state = await voting.getVote(voteId) + const voterState = await voting.getVoterState(voteId, holder29) + + assertBn(state[7], bigExp(29, decimals), 'nay vote should have been counted') + assert.equal(voterState, VOTER_STATE.NAY, 'holder29 should have nay voter status') + }) + + it('holder can object', async () => { + await voting.mockIncreaseTime(mainPhase + 1) + await assertRevert(voting.vote(voteId, true, false, { from: holder29 }), ERRORS.VOTING_CAN_NOT_VOTE) + await assertRevert(voting.vote(voteId, true, true, { from: holder29 }), ERRORS.VOTING_CAN_NOT_VOTE) + ;({ + open, executed, startDate, snapshotBlock, supportRequired, minAcceptQuorum, + yea, nay, votingPower, script, phase + } = await voting.getVote(voteId)) + assert.equal(yea, 0, 'should be no votes yet') + assert.equal(nay, 0, 'should be no votes yet') + + let tx = await voting.vote(voteId, false, false, { from: holder29 }) + assertEvent(tx, 'CastVote', { expectedArgs: { voteId: voteId, voter: holder29, supports: false }}) + assertEvent(tx, 'CastObjection', { expectedArgs: { voteId: voteId, voter: holder29 }}) + assertAmountOfEvents(tx, 'CastVote', { expectedAmount: 1 }) + assertAmountOfEvents(tx, 'CastObjection', { expectedAmount: 1 }) + + ;({ + open, executed, startDate, snapshotBlock, supportRequired, minAcceptQuorum, + yea, nay, votingPower, script, phase + } = await voting.getVote(voteId)) + assert.equal(yea, 0, 'should be no yea votes') + assert.notEqual(nay, 0, 'should some nay votes') + const nayBefore = nay + + await assertRevert(voting.vote(voteId, true, false, { from: holder29 }), ERRORS.VOTING_CAN_NOT_VOTE) + tx = await voting.vote(voteId, false, false, { from: holder29 }) + assertEvent(tx, 'CastVote', { expectedArgs: { voteId: voteId, voter: holder29, supports: false }}) + assertEvent(tx, 'CastObjection', { expectedArgs: { voteId: voteId, voter: holder29 }}) + assertAmountOfEvents(tx, 'CastVote', { expectedAmount: 1 }) + assertAmountOfEvents(tx, 'CastObjection', { expectedAmount: 1 }) + + assert.equal(yea, 0, 'should be no yea votes') + assert.equal(nay, nayBefore, 'should be same nay votes') + + await voting.mockIncreaseTime(objectionPhase) + }) + + it('canVote check works', async () => { + assert.equal(await voting.canVote(voteId, holder29), true, 'should be able to vote') + await voting.mockIncreaseTime(mainPhase + 1) + assert.equal(await voting.canVote(voteId, holder29), true, 'should be unable to vote') + await voting.mockIncreaseTime(objectionPhase) + assert.equal(await voting.canVote(voteId, holder29), false, 'should be unable to vote') + }) + + it('getVotePhase works', async () => { + const extractPhaseFromGetVote = async (voteId) => { + const phaseIndex = 10 + return (await voting.getVote(voteId))[phaseIndex] + } + + const MAIN_PHASE = 0 + assert.equal(await voting.getVotePhase(voteId), MAIN_PHASE, 'should be main phase') + assert.equal(await extractPhaseFromGetVote(voteId), MAIN_PHASE, 'should be main phase') + + await voting.mockIncreaseTime(mainPhase + 1) + const OBJECTION_PHASE = 1 + assert.equal(await voting.getVotePhase(voteId), OBJECTION_PHASE, 'should be objection phase') + assert.equal(await extractPhaseFromGetVote(voteId), OBJECTION_PHASE, 'should be objection phase') + + await voting.mockIncreaseTime(objectionPhase) + const CLOSED = 2 + assert.equal(await voting.getVotePhase(voteId), CLOSED, 'should be closed vote') + assert.equal(await extractPhaseFromGetVote(voteId), CLOSED, 'should be closed vote') + }) + + it('holder can modify vote', async () => { + let tx = await voting.vote(voteId, true, true, { from: holder29 }) + assertEvent(tx, 'CastVote', { expectedArgs: { voteId: voteId, voter: holder29, supports: true }}) + assertAmountOfEvents(tx, 'CastVote', { expectedAmount: 1 }) + assertAmountOfEvents(tx, 'CastObjection', { expectedAmount: 0 }) + + tx = await voting.vote(voteId, false, true, { from: holder29 }) + assertEvent(tx, 'CastVote', { expectedArgs: { voteId: voteId, voter: holder29, supports: false }}) + assertAmountOfEvents(tx, 'CastVote', { expectedAmount: 1 }) + assertAmountOfEvents(tx, 'CastObjection', { expectedAmount: 0 }) + + tx = await voting.vote(voteId, true, true, { from: holder29 }) + assertEvent(tx, 'CastVote', { expectedArgs: { voteId: voteId, voter: holder29, supports: true }}) + assertAmountOfEvents(tx, 'CastVote', { expectedAmount: 1 }) + assertAmountOfEvents(tx, 'CastObjection', { expectedAmount: 0 }) + + const state = await voting.getVote(voteId) + + assertBn(state[6], bigExp(29, decimals), 'yea vote should have been counted') + assert.equal(state[7], 0, 'nay vote should have been removed') + }) + + it('token transfers dont affect voting', async () => { + await token.transfer(nonHolder, bigExp(29, decimals), { from: holder29 }) + + await voting.vote(voteId, true, true, { from: holder29 }) + const state = await voting.getVote(voteId) + + assertBn(state[6], bigExp(29, decimals), 'yea vote should have been counted') + assert.equal(await token.balanceOf(holder29), 0, 'balance should be 0 at current block') + }) + + it('throws when non-holder votes', async () => { + await assertRevert(voting.vote(voteId, true, true, { from: nonHolder }), ERRORS.VOTING_NO_VOTING_POWER) + }) + + it('throws when voting after voting closes', async () => { + await voting.mockIncreaseTime(votingDuration + 1) + await assertRevert(voting.vote(voteId, true, true, { from: holder29 }), ERRORS.VOTING_CAN_NOT_VOTE) + }) + + it('can execute if vote is approved with support and quorum', async () => { + await voting.vote(voteId, true, true, { from: holder29 }) + await voting.vote(voteId, false, true, { from: holder20 }) + await voting.mockIncreaseTime(votingDuration + 1) + await voting.executeVote(voteId) + assert.equal(await executionTarget.counter(), 2, 'should have executed result') + }) + + it('cannot execute vote if not enough quorum met', async () => { + await voting.vote(voteId, true, true, { from: holder20 }) + await voting.mockIncreaseTime(votingDuration + 1) + await assertRevert(voting.executeVote(voteId), ERRORS.VOTING_CAN_NOT_EXECUTE) + }) + + it('cannot execute vote if not support met', async () => { + await voting.vote(voteId, false, true, { from: holder29 }) + await voting.vote(voteId, false, true, { from: holder20 }) + await voting.mockIncreaseTime(votingDuration + 1) + await assertRevert(voting.executeVote(voteId), ERRORS.VOTING_CAN_NOT_EXECUTE) + }) + + it('vote isnot executed automatically if decided', async () => { + await voting.vote(voteId, true, false, { from: holder51 }) // doesnt cause execution + assert.equal(await executionTarget.counter(), 0, 'should not have executed result') + }) + + it('cannot re-execute vote', async () => { + await voting.vote(voteId, true, true, { from: holder51 }) + await voting.mockIncreaseTime(votingDuration + 1) + await voting.executeVote(voteId) + + await assertRevert(voting.executeVote(voteId), ERRORS.VOTING_CAN_NOT_EXECUTE) + }) + + it('cannot vote on executed vote', async () => { + await voting.vote(voteId, true, true, { from: holder51 }) + await voting.mockIncreaseTime(votingDuration + 1) + await voting.executeVote(voteId) + + await assertRevert(voting.vote(voteId, true, true, { from: holder20 }), ERRORS.VOTING_CAN_NOT_VOTE) + }) + }) + }) + + context('voting for', () => { + let script, voteId, creator, metadata + + const neededSupport = pct16(50) + const minimumAcceptanceQuorum = pct16(20) - it("fails changing required support to 100% or more", async () => { - await assertRevert( - voting.changeSupportRequiredPct(pct16(101)), - ERRORS.VOTING_CHANGE_SUPP_TOO_BIG - ); - await assertRevert( - voting.changeSupportRequiredPct(pct16(100)), - ERRORS.VOTING_CHANGE_SUPP_TOO_BIG - ); - }); - - it("can change minimum acceptance quorum", async () => { - const receipt = await voting.changeMinAcceptQuorumPct(1); - assertAmountOfEvents(receipt, "ChangeMinQuorum"); - - assert.equal( - await voting.minAcceptQuorumPct(), - 1, - "should have changed acceptance quorum" - ); - }); - - it("fails changing minimum acceptance quorum to greater than min support", async () => { + beforeEach(async () => { + token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', decimals, 'n', true) // empty parameters minime + + await token.generateTokens(holder20, bigExp(20, decimals)) + await token.generateTokens(holder29, bigExp(29, decimals)) + await token.generateTokens(holder51, bigExp(51, decimals)) + await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, 0) + await voting.setDelegate(delegate1, {from: holder29}) + await voting.setDelegate(delegate1, {from: holder51}) + + executionTarget = await ExecutionTarget.new() + + const action = {to: executionTarget.address, calldata: executionTarget.contract.methods.execute().encodeABI()} + script = encodeCallScript([action, action]) + + const receipt = await voting.methods['newVote(bytes,string,bool,bool)'](script, 'metadata', false, false, {from: holder51}); + voteId = getEventArgument(receipt, 'StartVote', 'voteId') + creator = getEventArgument(receipt, 'StartVote', 'creator') + metadata = getEventArgument(receipt, 'StartVote', 'metadata') + }) + + + it('delegate can vote for voter', async () => { + const tx = await voting.voteFor(voteId, false, holder29, {from: delegate1}) + assertEvent(tx, 'CastVote', {expectedArgs: {voteId: voteId, voter: holder29, supports: false}}) + assertEvent(tx, 'CastVoteAsDelegate', {expectedArgs: {voteId: voteId, delegate: delegate1, voter: holder29, supports: false}}) + assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'CastObjection', {expectedAmount: 0}) + assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 1}) + + const state = await voting.getVote(voteId) + const voterState = await voting.getVoterState(voteId, holder29) + + assertBn(state[7], bigExp(29, decimals), 'nay vote should have been counted') + assert.equal(voterState, VOTER_STATE.DELEGATE_NAY, 'holder29 should have delegate nay voter status') + }) + + it('delegate can vote for both voters', async () => { + const tx = await voting.voteForMultiple(voteId, false, [holder29, holder51], {from: delegate1}) + assertEvent(tx, 'CastVote', {expectedArgs: {voteId: voteId, voter: holder29, supports: false}}) + assertEvent(tx, 'CastVote', {index: 1, expectedArgs: {voteId: voteId, voter: holder51, supports: false}}) + assertEvent(tx, 'CastVoteAsDelegate', {expectedArgs: {voteId: voteId, delegate: delegate1, voter: holder29, supports: false}}) + assertEvent(tx, 'CastVoteAsDelegate', {index: 1, expectedArgs: {voteId: voteId, delegate: delegate1, voter: holder51, supports: false}}) + assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 2}) + assertAmountOfEvents(tx, 'CastObjection', {expectedAmount: 0}) + assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 2}) + + const state = await voting.getVote(voteId) + assertBn(state[7], bigExp(80, decimals), 'nay vote should have been counted') + + const voterState29 = await voting.getVoterState(voteId, holder29) + assert.equal(voterState29, VOTER_STATE.DELEGATE_NAY, 'holder29 should have delegate nay voter status') + + const voterState51 = await voting.getVoterState(voteId, holder51) + assert.equal(voterState51, VOTER_STATE.DELEGATE_NAY, 'holder51 should have delegate nay voter status') + }) + + it(`delegate can't vote for both voters if one has previously voted`, async () => { + await voting.vote(voteId, false, true, { from: holder29 }) await assertRevert( - voting.changeMinAcceptQuorumPct(neededSupport.add(bn(1))), - ERRORS.VOTING_CHANGE_QUORUM_PCTS - ); - }); - }); + voting.voteForMultiple(voteId, false, [holder29, holder51], {from: delegate1}), + ERRORS.VOTING_CAN_NOT_VOTE_FOR + ) + }) - for (const decimals of [0, 2, 18, 26]) { - context(`normal token supply, ${decimals} decimals`, () => { - const neededSupport = pct16(50); - const minimumAcceptanceQuorum = pct16(20); + it(`voter can overwrite delegate's vote`, async () => { + await voting.voteFor(voteId, false, holder29, {from: delegate1}) - beforeEach(async () => { - token = await MiniMeToken.new( - ZERO_ADDRESS, - ZERO_ADDRESS, - 0, - "n", - decimals, - "n", - true - ); // empty parameters minime - - await token.generateTokens(holder20, bigExp(20, decimals)); - await token.generateTokens(holder29, bigExp(29, decimals)); - await token.generateTokens(holder51, bigExp(51, decimals)); - - await voting.initialize( - token.address, - neededSupport, - minimumAcceptanceQuorum, - votingDuration, - objectionPhase - ); - - executionTarget = await ExecutionTarget.new(); - }); - - it("execution scripts can execute multiple actions", async () => { - const action = { - to: executionTarget.address, - calldata: executionTarget.contract.methods.execute().encodeABI(), - }; - const script = encodeCallScript([action, action, action]); - - const voteId = createdVoteId( - await voting.newVote(script, "", { from: holder51 }) - ); - await voting.vote(voteId, true, true, { from: holder51 }); - await voting.mockIncreaseTime(votingDuration + 1); - await voting.executeVote(voteId); - - assert.equal( - await executionTarget.counter(), - 3, - "should have executed multiple times" - ); - }); - - it("execution script can be empty", async () => { - const voteId = createdVoteId( - await voting.newVote(encodeCallScript([]), "", { from: holder51 }) - ); - await voting.vote(voteId, true, true, { from: holder51 }); - await voting.mockIncreaseTime(votingDuration + 1); - await voting.executeVote(voteId); - }); - - it("check castVote do nothing (deprecated)", async () => { - let voteId = createdVoteId( - await voting.methods["newVote(bytes,string,bool,bool)"]( - encodeCallScript([]), - "", - true, - false, - { from: holder51 } - ) - ); - assert.equal( - await voting.getVoterState(voteId, holder51), - VOTER_STATE.ABSENT, - "holder51 should not have voted" - ); - }); - - it("check executesIfDecided do nothing (deprecated)", async () => { - let voteId = createdVoteId( - await voting.methods["newVote(bytes,string,bool,bool)"]( - encodeCallScript([]), - "", - true, - false, - { from: holder51 } - ) - ); - await voting.vote(voteId, true, true, { from: holder51 }); - assert.equal( - await voting.canExecute(voteId), - false, - "should be in the unexecuted state" - ); - await voting.mockIncreaseTime(votingDuration + 1); - await voting.executeVote(voteId); - assert.equal( - await voting.canExecute(voteId), - false, - "should be in the executed state" - ); - - voteId = createdVoteId( - await voting.methods["newVote(bytes,string,bool,bool)"]( - encodeCallScript([]), - "", - true, - true, - { from: holder51 } - ) - ); - await voting.vote(voteId, true, true, { from: holder51 }); - assert.equal( - await voting.canExecute(voteId), - false, - "should be in the unexecuted state" - ); - await voting.mockIncreaseTime(votingDuration + 1); - await voting.executeVote(voteId); - assert.equal( - await voting.canExecute(voteId), - false, - "should be in the executed state" - ); - }); - - it("execution throws if any action on script throws", async () => { - const action = { - to: executionTarget.address, - calldata: executionTarget.contract.methods.execute().encodeABI(), - }; - let script = encodeCallScript([action]); - script = script.slice(0, -2); // remove one byte from calldata for it to fail - - const voteId = createdVoteId( - await voting.newVote(script, "", { from: holder51 }) - ); - await voting.mockIncreaseTime(votingDuration + 1); - await assertRevert(voting.executeVote(voteId)); - }); - - it("forwarding creates vote", async () => { - const action = { - to: executionTarget.address, - calldata: executionTarget.contract.methods.execute().encodeABI(), - }; - const script = encodeCallScript([action]); - const voteId = createdVoteId( - await voting.forward(script, { from: holder51 }) - ); - assert.equal(voteId, 0, "voting should have been created"); - }); - - context("creating vote", () => { - let script, voteId, creator, metadata; - - beforeEach(async () => { - const action = { - to: executionTarget.address, - calldata: executionTarget.contract.methods.execute().encodeABI(), - }; - script = encodeCallScript([action, action]); - - const receipt = await voting.methods[ - "newVote(bytes,string,bool,bool)" - ](script, "metadata", false, false, { from: holder51 }); - voteId = getEventArgument(receipt, "StartVote", "voteId"); - creator = getEventArgument(receipt, "StartVote", "creator"); - metadata = getEventArgument(receipt, "StartVote", "metadata"); - }); - - it("has correct state", async () => { - const { - open, - executed, - snapshotBlock, - supportRequired, - minAcceptQuorum, - yea, - nay, - votingPower, - script: execScript, - } = await voting.getVote(voteId); - assert.isTrue(open, "vote should be open"); - assert.isFalse(executed, "vote should not be executed"); - assert.equal(creator, holder51, "creator should be correct"); - assertBn( - snapshotBlock, - (await web3.eth.getBlockNumber()) - 1, - "snapshot block should be correct" - ); - assertBn( - supportRequired, - neededSupport, - "required support should be app required support" - ); - assertBn( - minAcceptQuorum, - minimumAcceptanceQuorum, - "min quorum should be app min quorum" - ); - assertBn(yea, 0, "initial yea should be 0"); - assertBn(nay, 0, "initial nay should be 0"); - assertBn( - votingPower, - bigExp(100, decimals), - "voting power should be 100" - ); - assert.equal(execScript, script, "script should be correct"); - assert.equal( - metadata, - "metadata", - "should have returned correct metadata" - ); - assert.equal( - await voting.getVoterState(voteId, nonHolder), - VOTER_STATE.ABSENT, - "nonHolder should not have voted" - ); - }); - - it("fails getting a vote out of bounds", async () => { - await assertRevert( - voting.getVote(voteId + 1), - ERRORS.VOTING_NO_VOTE - ); - }); - - it("changing required support does not affect vote required support", async () => { - await voting.changeSupportRequiredPct(pct16(70)); - - // With previous required support at 50%, vote should be approved - // with new quorum at 70% it shouldn't have, but since min quorum is snapshotted - // it will succeed - - await voting.vote(voteId, true, false, { from: holder51 }); - await voting.vote(voteId, true, false, { from: holder20 }); - await voting.vote(voteId, false, false, { from: holder29 }); - await voting.mockIncreaseTime(votingDuration + 1); - - const state = await voting.getVote(voteId); - assertBn( - state[4], - neededSupport, - "required support in vote should stay equal" - ); - await voting.executeVote(voteId); // exec doesn't fail - }); - - it("changing min quorum doesnt affect vote min quorum", async () => { - await voting.changeMinAcceptQuorumPct(pct16(50)); - - // With previous min acceptance quorum at 20%, vote should be approved - // with new quorum at 50% it shouldn't have, but since min quorum is snapshotted - // it will succeed - - const tx = await voting.vote(voteId, true, true, { - from: holder29, - }); - assertEvent(tx, "CastVote", { - expectedArgs: { voteId: voteId, voter: holder29, supports: true }, - }); - assertAmountOfEvents(tx, "CastVote", { expectedAmount: 1 }); - assertAmountOfEvents(tx, "CastObjection", { expectedAmount: 0 }); - - await voting.mockIncreaseTime(votingDuration + 1); - - const state = await voting.getVote(voteId); - assertBn( - state[5], - minimumAcceptanceQuorum, - "acceptance quorum in vote should stay equal" - ); - await voting.executeVote(voteId); // exec doesn't fail - }); - - it("holder can vote", async () => { - const tx = await voting.vote(voteId, false, true, { - from: holder29, - }); - assertEvent(tx, "CastVote", { - expectedArgs: { - voteId: voteId, - voter: holder29, - supports: false, - }, - }); - assertAmountOfEvents(tx, "CastVote", { expectedAmount: 1 }); - assertAmountOfEvents(tx, "CastObjection", { expectedAmount: 0 }); - - const state = await voting.getVote(voteId); - const voterState = await voting.getVoterState(voteId, holder29); - - assertBn( - state[7], - bigExp(29, decimals), - "nay vote should have been counted" - ); - assert.equal( - voterState, - VOTER_STATE.NAY, - "holder29 should have nay voter status" - ); - }); - - it("holder can object", async () => { - await voting.mockIncreaseTime(mainPhase + 1); - await assertRevert( - voting.vote(voteId, true, false, { from: holder29 }), - ERRORS.VOTING_CAN_NOT_VOTE - ); - await assertRevert( - voting.vote(voteId, true, true, { from: holder29 }), - ERRORS.VOTING_CAN_NOT_VOTE - ); - ({ - open, - executed, - startDate, - snapshotBlock, - supportRequired, - minAcceptQuorum, - yea, - nay, - votingPower, - script, - phase, - } = await voting.getVote(voteId)); - assert.equal(yea, 0, "should be no votes yet"); - assert.equal(nay, 0, "should be no votes yet"); - - let tx = await voting.vote(voteId, false, false, { - from: holder29, - }); - assertEvent(tx, "CastVote", { - expectedArgs: { - voteId: voteId, - voter: holder29, - supports: false, - }, - }); - assertEvent(tx, "CastObjection", { - expectedArgs: { voteId: voteId, voter: holder29 }, - }); - assertAmountOfEvents(tx, "CastVote", { expectedAmount: 1 }); - assertAmountOfEvents(tx, "CastObjection", { expectedAmount: 1 }); - ({ - open, - executed, - startDate, - snapshotBlock, - supportRequired, - minAcceptQuorum, - yea, - nay, - votingPower, - script, - phase, - } = await voting.getVote(voteId)); - assert.equal(yea, 0, "should be no yea votes"); - assert.notEqual(nay, 0, "should some nay votes"); - const nayBefore = nay; - - await assertRevert( - voting.vote(voteId, true, false, { from: holder29 }), - ERRORS.VOTING_CAN_NOT_VOTE - ); - tx = await voting.vote(voteId, false, false, { from: holder29 }); - assertEvent(tx, "CastVote", { - expectedArgs: { - voteId: voteId, - voter: holder29, - supports: false, - }, - }); - assertEvent(tx, "CastObjection", { - expectedArgs: { voteId: voteId, voter: holder29 }, - }); - assertAmountOfEvents(tx, "CastVote", { expectedAmount: 1 }); - assertAmountOfEvents(tx, "CastObjection", { expectedAmount: 1 }); - - assert.equal(yea, 0, "should be no yea votes"); - assert.equal(nay, nayBefore, "should be same nay votes"); - - await voting.mockIncreaseTime(objectionPhase); - }); - - it("canVote check works", async () => { - assert.equal( - await voting.canVote(voteId, holder29), - true, - "should be able to vote" - ); - await voting.mockIncreaseTime(mainPhase + 1); - assert.equal( - await voting.canVote(voteId, holder29), - true, - "should be unable to vote" - ); - await voting.mockIncreaseTime(objectionPhase); - assert.equal( - await voting.canVote(voteId, holder29), - false, - "should be unable to vote" - ); - }); - - it("getVotePhase works", async () => { - const extractPhaseFromGetVote = async (voteId) => { - const phaseIndex = 10; - return (await voting.getVote(voteId))[phaseIndex]; - }; - - const MAIN_PHASE = 0; - assert.equal( - await voting.getVotePhase(voteId), - MAIN_PHASE, - "should be main phase" - ); - assert.equal( - await extractPhaseFromGetVote(voteId), - MAIN_PHASE, - "should be main phase" - ); - - await voting.mockIncreaseTime(mainPhase + 1); - const OBJECTION_PHASE = 1; - assert.equal( - await voting.getVotePhase(voteId), - OBJECTION_PHASE, - "should be objection phase" - ); - assert.equal( - await extractPhaseFromGetVote(voteId), - OBJECTION_PHASE, - "should be objection phase" - ); - - await voting.mockIncreaseTime(objectionPhase); - const CLOSED = 2; - assert.equal( - await voting.getVotePhase(voteId), - CLOSED, - "should be closed vote" - ); - assert.equal( - await extractPhaseFromGetVote(voteId), - CLOSED, - "should be closed vote" - ); - }); - - it("holder can modify vote", async () => { - let tx = await voting.vote(voteId, true, true, { from: holder29 }); - assertEvent(tx, "CastVote", { - expectedArgs: { voteId: voteId, voter: holder29, supports: true }, - }); - assertAmountOfEvents(tx, "CastVote", { expectedAmount: 1 }); - assertAmountOfEvents(tx, "CastObjection", { expectedAmount: 0 }); - - tx = await voting.vote(voteId, false, true, { from: holder29 }); - assertEvent(tx, "CastVote", { - expectedArgs: { - voteId: voteId, - voter: holder29, - supports: false, - }, - }); - assertAmountOfEvents(tx, "CastVote", { expectedAmount: 1 }); - assertAmountOfEvents(tx, "CastObjection", { expectedAmount: 0 }); - - tx = await voting.vote(voteId, true, true, { from: holder29 }); - assertEvent(tx, "CastVote", { - expectedArgs: { voteId: voteId, voter: holder29, supports: true }, - }); - assertAmountOfEvents(tx, "CastVote", { expectedAmount: 1 }); - assertAmountOfEvents(tx, "CastObjection", { expectedAmount: 0 }); - - const state = await voting.getVote(voteId); - - assertBn( - state[6], - bigExp(29, decimals), - "yea vote should have been counted" - ); - assert.equal(state[7], 0, "nay vote should have been removed"); - }); - - it("token transfers dont affect voting", async () => { - await token.transfer(nonHolder, bigExp(29, decimals), { - from: holder29, - }); - - await voting.vote(voteId, true, true, { from: holder29 }); - const state = await voting.getVote(voteId); - - assertBn( - state[6], - bigExp(29, decimals), - "yea vote should have been counted" - ); - assert.equal( - await token.balanceOf(holder29), - 0, - "balance should be 0 at current block" - ); - }); - - it("throws when non-holder votes", async () => { - await assertRevert( - voting.vote(voteId, true, true, { from: nonHolder }), - ERRORS.VOTING_NO_VOTING_POWER - ); - }); - - it("throws when voting after voting closes", async () => { - await voting.mockIncreaseTime(votingDuration + 1); - await assertRevert( - voting.vote(voteId, true, true, { from: holder29 }), - ERRORS.VOTING_CAN_NOT_VOTE - ); - }); - - it("can execute if vote is approved with support and quorum", async () => { - await voting.vote(voteId, true, true, { from: holder29 }); - await voting.vote(voteId, false, true, { from: holder20 }); - await voting.mockIncreaseTime(votingDuration + 1); - await voting.executeVote(voteId); - assert.equal( - await executionTarget.counter(), - 2, - "should have executed result" - ); - }); - - it("cannot execute vote if not enough quorum met", async () => { - await voting.vote(voteId, true, true, { from: holder20 }); - await voting.mockIncreaseTime(votingDuration + 1); - await assertRevert( - voting.executeVote(voteId), - ERRORS.VOTING_CAN_NOT_EXECUTE - ); - }); - - it("cannot execute vote if not support met", async () => { - await voting.vote(voteId, false, true, { from: holder29 }); - await voting.vote(voteId, false, true, { from: holder20 }); - await voting.mockIncreaseTime(votingDuration + 1); - await assertRevert( - voting.executeVote(voteId), - ERRORS.VOTING_CAN_NOT_EXECUTE - ); - }); - - it("vote isnot executed automatically if decided", async () => { - await voting.vote(voteId, true, false, { from: holder51 }); // doesnt cause execution - assert.equal( - await executionTarget.counter(), - 0, - "should not have executed result" - ); - }); - - it("cannot re-execute vote", async () => { - await voting.vote(voteId, true, true, { from: holder51 }); - await voting.mockIncreaseTime(votingDuration + 1); - await voting.executeVote(voteId); - - await assertRevert( - voting.executeVote(voteId), - ERRORS.VOTING_CAN_NOT_EXECUTE - ); - }); - - it("cannot vote on executed vote", async () => { - await voting.vote(voteId, true, true, { from: holder51 }); - await voting.mockIncreaseTime(votingDuration + 1); - await voting.executeVote(voteId); - - await assertRevert( - voting.vote(voteId, true, true, { from: holder20 }), - ERRORS.VOTING_CAN_NOT_VOTE - ); - }); - }); - }); - - context("voting for", () => { - let script, voteId, creator, metadata; - - const neededSupport = pct16(50); - const minimumAcceptanceQuorum = pct16(20); + const tx = await voting.vote(voteId, true, true, {from: holder29}) + assertEvent(tx, 'CastVote', {expectedArgs: {voteId: voteId, voter: holder29, supports: true}}) + assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'CastObjection', {expectedAmount: 0}) - beforeEach(async () => { - token = await MiniMeToken.new( - ZERO_ADDRESS, - ZERO_ADDRESS, - 0, - "n", - decimals, - "n", - true - ); // empty parameters minime - - await token.generateTokens(holder20, bigExp(20, decimals)); - await token.generateTokens(holder29, bigExp(29, decimals)); - await token.generateTokens(holder51, bigExp(51, decimals)); - await voting.initialize( - token.address, - neededSupport, - minimumAcceptanceQuorum, - votingDuration, - 0 - ); - await voting.setDelegate(delegate1, { from: holder29 }); - await voting.setDelegate(delegate1, { from: holder51 }); - - executionTarget = await ExecutionTarget.new(); - - const action = { - to: executionTarget.address, - calldata: executionTarget.contract.methods.execute().encodeABI(), - }; - script = encodeCallScript([action, action]); - - const receipt = await voting.methods[ - "newVote(bytes,string,bool,bool)" - ](script, "metadata", false, false, { from: holder51 }); - voteId = getEventArgument(receipt, "StartVote", "voteId"); - creator = getEventArgument(receipt, "StartVote", "creator"); - metadata = getEventArgument(receipt, "StartVote", "metadata"); - }); - - it("delegate can vote for voter", async () => { - const tx = await voting.voteFor(voteId, false, holder29, { - from: delegate1, - }); - assertEvent(tx, "CastVote", { - expectedArgs: { voteId: voteId, voter: holder29, supports: false }, - }); - assertEvent(tx, "CastVoteAsDelegate", { - expectedArgs: { - voteId: voteId, - delegate: delegate1, - voter: holder29, - supports: false, - }, - }); - assertAmountOfEvents(tx, "CastVote", { expectedAmount: 1 }); - assertAmountOfEvents(tx, "CastObjection", { expectedAmount: 0 }); - assertAmountOfEvents(tx, "CastVoteAsDelegate", { expectedAmount: 1 }); - - const state = await voting.getVote(voteId); - const voterState = await voting.getVoterState(voteId, holder29); - - assertBn( - state[7], - bigExp(29, decimals), - "nay vote should have been counted" - ); - assert.equal( - voterState, - VOTER_STATE.DELEGATE_NAY, - "holder29 should have delegate nay voter status" - ); - }); - - it("delegate can vote for both voters", async () => { - const tx = await voting.voteForMultiple( - voteId, - false, - [holder29, holder51], - { from: delegate1 } - ); - assertEvent(tx, "CastVote", { - expectedArgs: { voteId: voteId, voter: holder29, supports: false }, - }); - assertEvent(tx, "CastVote", { - index: 1, - expectedArgs: { voteId: voteId, voter: holder51, supports: false }, - }); - assertEvent(tx, "CastVoteAsDelegate", { - expectedArgs: { - voteId: voteId, - delegate: delegate1, - voter: holder29, - supports: false, - }, - }); - assertEvent(tx, "CastVoteAsDelegate", { - index: 1, - expectedArgs: { - voteId: voteId, - delegate: delegate1, - voter: holder51, - supports: false, - }, - }); - assertAmountOfEvents(tx, "CastVote", { expectedAmount: 2 }); - assertAmountOfEvents(tx, "CastObjection", { expectedAmount: 0 }); - assertAmountOfEvents(tx, "CastVoteAsDelegate", { expectedAmount: 2 }); - - const state = await voting.getVote(voteId); - assertBn( - state[7], - bigExp(80, decimals), - "nay vote should have been counted" - ); - - const voterState29 = await voting.getVoterState(voteId, holder29); - assert.equal( - voterState29, - VOTER_STATE.DELEGATE_NAY, - "holder29 should have delegate nay voter status" - ); - - const voterState51 = await voting.getVoterState(voteId, holder51); - assert.equal( - voterState51, - VOTER_STATE.DELEGATE_NAY, - "holder51 should have delegate nay voter status" - ); - }); - - it(`delegate can't vote for both voters if one has previously voted`, async () => { - await voting.vote(voteId, false, true, { from: holder29 }); - await assertRevert( - voting.voteForMultiple(voteId, false, [holder29, holder51], { - from: delegate1, - }), - ERRORS.VOTING_CAN_NOT_VOTE_FOR - ); - }); - - it(`voter can overwrite delegate's vote`, async () => { - await voting.voteFor(voteId, false, holder29, { from: delegate1 }); - - const tx = await voting.vote(voteId, true, true, { from: holder29 }); - assertEvent(tx, "CastVote", { - expectedArgs: { voteId: voteId, voter: holder29, supports: true }, - }); - assertAmountOfEvents(tx, "CastVote", { expectedAmount: 1 }); - assertAmountOfEvents(tx, "CastObjection", { expectedAmount: 0 }); - - const state = await voting.getVote(voteId); - assertBn( - state[6], - bigExp(29, decimals), - "yea vote should have been counted" - ); - assertBn( - state[7], - bigExp(0, decimals), - "nay vote should have been reset" - ); - - const voterState29 = await voting.getVoterState(voteId, holder29); - assert.equal( - voterState29, - VOTER_STATE.YEA, - "holder29 should have yea voter status" - ); - }); - - it(`delegate can't overwrite voter's vote`, async () => { - await voting.voteFor(voteId, false, holder29, { from: delegate1 }); - await voting.vote(voteId, true, true, { from: holder29 }); - - await assertRevert( - voting.voteFor(voteId, false, holder29, { from: delegate1 }), - ERRORS.VOTING_CAN_NOT_VOTE_FOR - ); - }); - }); - } - - context("wrong initializations", () => { - beforeEach(async () => { - token = await MiniMeToken.new( - ZERO_ADDRESS, - ZERO_ADDRESS, - 0, - "n", - 0, - "n", - true - ); // empty parameters minime - }); - - it("fails if voteTime less or equal to objectionTime", async () => { - let badVoteTime = objectionPhase; - const neededSupport = pct16(50); - const minimumAcceptanceQuorum = pct16(20); + const state = await voting.getVote(voteId) + assertBn(state[6], bigExp(29, decimals), 'yea vote should have been counted') + assertBn(state[7], bigExp(0, decimals), 'nay vote should have been reset') - await assertRevert( - voting.initialize( - token.address, - neededSupport, - minimumAcceptanceQuorum, - badVoteTime, - objectionPhase - ), - ERRORS.VOTING_INIT_OBJ_TIME_TOO_BIG - ); - - badVoteTime = objectionPhase / 2; - await assertRevert( - voting.initialize( - token.address, - neededSupport, - minimumAcceptanceQuorum, - badVoteTime, - objectionPhase - ), - ERRORS.VOTING_INIT_OBJ_TIME_TOO_BIG - ); - }); - - it("fails if min acceptance quorum is greater than min support", async () => { - const neededSupport = pct16(20); - const minimumAcceptanceQuorum = pct16(50); - await assertRevert( - voting.initialize( - token.address, - neededSupport, - minimumAcceptanceQuorum, - votingDuration, - objectionPhase - ), - ERRORS.VOTING_INIT_PCTS - ); - }); - - it("fails if min support is 100% or more", async () => { - const minimumAcceptanceQuorum = pct16(20); - await assertRevert( - voting.initialize( - token.address, - pct16(101), - minimumAcceptanceQuorum, - votingDuration, - objectionPhase - ), - ERRORS.VOTING_INIT_SUPPORT_TOO_BIG - ); - await assertRevert( - voting.initialize( - token.address, - pct16(100), - minimumAcceptanceQuorum, - votingDuration, - objectionPhase - ), - ERRORS.VOTING_INIT_SUPPORT_TOO_BIG - ); - }); - }); - - context("empty token", () => { - const neededSupport = pct16(50); - const minimumAcceptanceQuorum = pct16(20); + const voterState29 = await voting.getVoterState(voteId, holder29) + assert.equal(voterState29, VOTER_STATE.YEA, 'holder29 should have yea voter status') + }) - beforeEach(async () => { - token = await MiniMeToken.new( - ZERO_ADDRESS, - ZERO_ADDRESS, - 0, - "n", - 0, - "n", - true - ); // empty parameters minime - - await voting.initialize( - token.address, - neededSupport, - minimumAcceptanceQuorum, - votingDuration, - objectionPhase - ); - }); + it(`delegate can't overwrite voter's vote`, async () => { + await voting.voteFor(voteId, false, holder29, {from: delegate1}) + await voting.vote(voteId, true, true, {from: holder29}) - it("fails creating a vote if token has no holder", async () => { await assertRevert( - voting.newVote(EMPTY_CALLS_SCRIPT, "metadata"), - ERRORS.VOTING_NO_VOTING_POWER - ); - }); - }); + voting.voteFor( + voteId, + false, + holder29, + { from: delegate1 } + ), ERRORS.VOTING_CAN_NOT_VOTE_FOR + ) + }) + }) + } - context("token supply = 1", () => { - const neededSupport = pct16(50); - const minimumAcceptanceQuorum = pct16(20); + context('wrong initializations', () => { + beforeEach(async() => { + token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', 0, 'n', true) // empty parameters minime + }) - beforeEach(async () => { - token = await MiniMeToken.new( - ZERO_ADDRESS, - ZERO_ADDRESS, - 0, - "n", - 0, - "n", - true - ); // empty parameters minime - - await token.generateTokens(holder1, 1); - - await voting.initialize( - token.address, - neededSupport, - minimumAcceptanceQuorum, - votingDuration, - objectionPhase - ); - }); - - it("new vote cannot be executed before voting", async () => { - // Account creating vote does not have any tokens and therefore doesn't vote - const voteId = createdVoteId( - await voting.newVote(EMPTY_CALLS_SCRIPT, "metadata") - ); - - assert.isFalse( - await voting.canExecute(voteId), - "vote cannot be executed" - ); - - await voting.vote(voteId, true, true, { from: holder1 }); - assert.isFalse( - await voting.canExecute(voteId), - "vote cannot be executed" - ); - - await voting.mockIncreaseTime(votingDuration + 1); - assert.isTrue(await voting.canExecute(voteId), "vote may be executed"); - - await voting.executeVote(voteId); - - const { open, executed } = await voting.getVote(voteId); - assert.isFalse(open, "vote should be closed"); - assert.isTrue(executed, "vote should have been executed"); - }); - - context("new vote parameters", () => { - it("creating vote as holder does not execute vote (even if _canExecute param says so)", async () => { - const voteId = createdVoteId( - await voting.methods["newVote(bytes,string,bool,bool)"]( - EMPTY_CALLS_SCRIPT, - "metadata", - true, - true, - { from: holder1 } - ) - ); - - const { open, executed } = await voting.getVote(voteId); - assert.isTrue(open, "vote should be closed"); - assert.isFalse(executed, "vote should have been executed"); - }); - - it("creating vote as holder doesn't execute vote if _canExecute param doesn't says so", async () => { - const voteId = createdVoteId( - await voting.methods["newVote(bytes,string,bool,bool)"]( - EMPTY_CALLS_SCRIPT, - "metadata", - true, - false, - { from: holder1 } - ) - ); - - const { open, executed } = await voting.getVote(voteId); - assert.isTrue(open, "vote should be open"); - assert.isFalse(executed, "vote should not have been executed"); - }); - }); - }); - - context("token supply = 3", () => { - const neededSupport = pct16(34); - const minimumAcceptanceQuorum = pct16(20); + it('fails if voteTime less or equal to objectionTime', async () => { + let badVoteTime = objectionPhase + const neededSupport = pct16(50) + const minimumAcceptanceQuorum = pct16(20) - beforeEach(async () => { - token = await MiniMeToken.new( - ZERO_ADDRESS, - ZERO_ADDRESS, - 0, - "n", - 0, - "n", - true - ); // empty parameters minime - - await token.generateTokens(holder1, 1); - await token.generateTokens(holder2, 2); - - await voting.initialize( + await assertRevert( + voting.initialize( token.address, neededSupport, minimumAcceptanceQuorum, - votingDuration, + badVoteTime, objectionPhase - ); - }); - - it("new vote cannot be executed before holder2 voting and time pass", async () => { - const voteId = createdVoteId( - await voting.newVote(EMPTY_CALLS_SCRIPT, "metadata") - ); - assert.isFalse( - await voting.canExecute(voteId), - "vote cannot be executed" - ); - assert.isFalse( - (await voting.getVote(voteId)).executed, - "vote should not have been executed" - ); - - await voting.vote(voteId, true, true, { from: holder1 }); - assert.isFalse( - await voting.canExecute(voteId), - "vote cannot be executed" - ); - assert.isFalse( - (await voting.getVote(voteId)).executed, - "vote should not have been executed" - ); - - await voting.vote(voteId, true, true, { from: holder2 }); - assert.isFalse( - await voting.canExecute(voteId), - "vote cannot be executed" - ); - assert.isFalse( - (await voting.getVote(voteId)).executed, - "vote should not have been executed" - ); - - await voting.mockIncreaseTime(votingDuration + 1); - assert.isTrue(await voting.canExecute(voteId), "vote may be executed"); - - await voting.executeVote(voteId); - - const { open, executed } = await voting.getVote(voteId); - assert.isFalse(open, "vote should be closed"); - assert.isTrue(executed, "vote should have been executed"); - }); - - it("creating vote as holder2 does not execute vote", async () => { - const voteId = createdVoteId( - await voting.newVote(EMPTY_CALLS_SCRIPT, "metadata", { - from: holder2, - }) - ); - - const { open, executed } = await voting.getVote(voteId); - assert.isTrue(open, "vote should be closed"); - assert.isFalse(executed, "vote should have been executed"); - }); - }); - - context("changing token supply", () => { - const neededSupport = pct16(50); - const minimumAcceptanceQuorum = pct16(20); + ), ERRORS.VOTING_INIT_OBJ_TIME_TOO_BIG + ) - beforeEach(async () => { - token = await MiniMeToken.new( - ZERO_ADDRESS, - ZERO_ADDRESS, - 0, - "n", - 0, - "n", - true - ); // empty parameters minime - - await token.generateTokens(holder1, 1); - await token.generateTokens(holder2, 1); - - await voting.initialize( + badVoteTime = objectionPhase / 2 + await assertRevert( + voting.initialize( token.address, neededSupport, minimumAcceptanceQuorum, - votingDuration, + badVoteTime, objectionPhase - ); - }); - - it("uses the correct snapshot value if tokens are minted afterwards", async () => { - // Create vote and afterwards generate some tokens - const voteId = createdVoteId( - await voting.newVote(EMPTY_CALLS_SCRIPT, "metadata") - ); - await token.generateTokens(holder2, 1); - - const { snapshotBlock, votingPower } = await voting.getVote(voteId); - - // Generating tokens advanced the block by one - assertBn( - snapshotBlock, - (await web3.eth.getBlockNumber()) - 2, - "snapshot block should be correct" - ); - assertBn( - votingPower, - await token.totalSupplyAt(snapshotBlock), - "voting power should match snapshot supply" - ); - assertBn(votingPower, 2, "voting power should be correct"); - }); - - it("uses the correct snapshot value if tokens are minted in the same block", async () => { - // Create vote and generate some tokens in the same transaction - // Requires the voting mock to be the token's owner - await token.changeController(voting.address); - const voteId = createdVoteId( - await voting.newTokenAndVote(holder2, 1, "metadata") - ); - - const { snapshotBlock, votingPower } = await voting.getVote(voteId); - assertBn( - snapshotBlock, - (await web3.eth.getBlockNumber()) - 1, - "snapshot block should be correct" - ); - assertBn( - votingPower, - await token.totalSupplyAt(snapshotBlock), - "voting power should match snapshot supply" - ); - assertBn(votingPower, 2, "voting power should be correct"); - }); - }); - - context("before init", () => { - it("fails creating a vote before initialization", async () => { - await assertRevert( - voting.newVote(encodeCallScript([]), ""), - ERRORS.APP_AUTH_FAILED - ); - }); - - it("fails to forward actions before initialization", async () => { - const action = { - to: executionTarget.address, - calldata: executionTarget.contract.methods.execute().encodeABI(), - }; - const script = encodeCallScript([action]); - await assertRevert( - voting.forward(script, { from: holder51 }), - ERRORS.VOTING_CAN_NOT_FORWARD - ); - }); - }); - - context("isValuePct unit test", async () => { - it("tests total = 0", async () => { - const result1 = await voting.isValuePct(0, 0, pct16(50)); - assert.equal(result1, false, "total 0 should always return false"); - const result2 = await voting.isValuePct(1, 0, pct16(50)); - assert.equal(result2, false, "total 0 should always return false"); - }); - - it("tests value = 0", async () => { - const result1 = await voting.isValuePct(0, 10, pct16(50)); - assert.equal(result1, false, "value 0 should false if pct is non-zero"); - const result2 = await voting.isValuePct(0, 10, 0); - assert.equal( - result2, - false, - "value 0 should return false if pct is zero" - ); - }); - - it("tests pct ~= 100", async () => { - const result1 = await voting.isValuePct(10, 10, pct16(100).sub(bn(1))); - assert.equal(result1, true, "value 10 over 10 should pass"); - }); - - it("tests strict inequality", async () => { - const result1 = await voting.isValuePct(10, 20, pct16(50)); - assert.equal( - result1, - false, - "value 10 over 20 should not pass for 50%" - ); - - const result2 = await voting.isValuePct( - pct16(50).sub(bn(1)), - pct16(100), - pct16(50) - ); - assert.equal(result2, false, "off-by-one down should not pass"); - - const result3 = await voting.isValuePct( - pct16(50).add(bn(1)), - pct16(100), - pct16(50) - ); - assert.equal(result3, true, "off-by-one up should pass"); - }); - }); - - context("unsafe time change", () => { - const neededSupport = pct16(1); - const minimumAcceptanceQuorum = pct16(1); + ), ERRORS.VOTING_INIT_OBJ_TIME_TOO_BIG + ) + }) - beforeEach(async () => { - token = await MiniMeToken.new( - ZERO_ADDRESS, - ZERO_ADDRESS, - 0, - "n", - 18, - "n", - true - ); - await token.generateTokens(holder20, bigExp(20, 18)); - await token.generateTokens(holder29, bigExp(29, 18)); - await token.generateTokens(holder51, bigExp(51, 18)); - await voting.initialize( - token.address, - neededSupport, - minimumAcceptanceQuorum, - votingDuration, - 0 - ); - executionTarget = await ExecutionTarget.new(); - }); - - it("reverts on non-authorized vote time change request", async () => { - await aclP.revokePermission( - ANY_ENTITY, - voting.address, - UNSAFELY_MODIFY_VOTE_TIME_ROLE, - { from: root } - ); - - const entities = [nonHolder, holder1, holder29, holder51]; - - for (ind = 0; ind < entities.length; ++ind) { - await assertRevert( - voting.unsafelyChangeVoteTime(500, { from: entities[ind] }), - "APP_AUTH_FAILED" - ); - } - }); - - it("simple change vote time", async () => { - const smallest = 1; - const increasingTime = 1500; - const decreasingTime = 500; - - // Allow to setting to smallest - receipt = await voting.unsafelyChangeVoteTime(smallest); - assertAmountOfEvents(receipt, "ChangeVoteTime"); - assert.equal( - await voting.voteTime(), - smallest, - "should have changed acceptance time" - ); - - // Allow to increasing voteTime - receipt = await voting.unsafelyChangeVoteTime(increasingTime); - assertAmountOfEvents(receipt, "ChangeVoteTime"); - assert.equal( - await voting.voteTime(), - increasingTime, - "should have changed acceptance time" - ); - - // Allow to decreasing voteTime - receipt = await voting.unsafelyChangeVoteTime(decreasingTime); - assertAmountOfEvents(receipt, "ChangeVoteTime"); - assert.equal( - await voting.voteTime(), - decreasingTime, - "should have changed acceptance time" - ); - }); - - it("reverts on non-authorized obj time change request", async () => { - await aclP.revokePermission( - ANY_ENTITY, - voting.address, - UNSAFELY_MODIFY_VOTE_TIME_ROLE, - { from: root } - ); - - const entities = [nonHolder, holder1, holder29, holder51]; - - for (ind = 0; ind < entities.length; ++ind) { - await assertRevert( - voting.unsafelyChangeObjectionPhaseTime(100, { - from: entities[ind], - }), - "APP_AUTH_FAILED" - ); - } - }); - - it("simple change obj time", async () => { - const increasingTime = 999; - const zeroTime = 0; - const decreasingTime = 500; - - // Allow to setting to zero - receipt = await voting.unsafelyChangeObjectionPhaseTime(increasingTime); - assertAmountOfEvents(receipt, "ChangeObjectionPhaseTime"); - assert.equal( - await voting.objectionPhaseTime(), - increasingTime, - "should have changed acceptance time" - ); - - // Allow to increasing voteTime - receipt = await voting.unsafelyChangeObjectionPhaseTime(zeroTime); - assertAmountOfEvents(receipt, "ChangeObjectionPhaseTime"); - assert.equal( - await voting.objectionPhaseTime(), - zeroTime, - "should have changed acceptance time" - ); - - // Allow to decreasing voteTime - receipt = await voting.unsafelyChangeObjectionPhaseTime(decreasingTime); - assertAmountOfEvents(receipt, "ChangeObjectionPhaseTime"); - assert.equal( - await voting.objectionPhaseTime(), - decreasingTime, - "should have changed acceptance time" - ); - }); - - it("reverts if voteTime < objectionTime", async () => { - await assertRevert( - voting.unsafelyChangeObjectionPhaseTime(votingDuration + 1), - ERRORS.VOTING_OBJ_TIME_TOO_BIG - ); + it('fails if min acceptance quorum is greater than min support', async () => { + const neededSupport = pct16(20) + const minimumAcceptanceQuorum = pct16(50) + await assertRevert(voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, + objectionPhase), ERRORS.VOTING_INIT_PCTS) + }) - await voting.unsafelyChangeObjectionPhaseTime(votingDuration - 1); + it('fails if min support is 100% or more', async () => { + const minimumAcceptanceQuorum = pct16(20) + await assertRevert(voting.initialize(token.address, pct16(101), minimumAcceptanceQuorum, votingDuration, + objectionPhase), ERRORS.VOTING_INIT_SUPPORT_TOO_BIG) + await assertRevert(voting.initialize(token.address, pct16(100), minimumAcceptanceQuorum, votingDuration, + objectionPhase), ERRORS.VOTING_INIT_SUPPORT_TOO_BIG) + }) + }) - await assertRevert( - voting.unsafelyChangeVoteTime(votingDuration - 1), - ERRORS.VOTING_VOTE_TIME_TOO_SMALL - ); - await assertRevert( - voting.unsafelyChangeVoteTime(0), - ERRORS.VOTING_VOTE_TIME_TOO_SMALL - ); - }); - - it("re-open finished vote through changing of voting time", async () => { - await voting.unsafelyChangeVoteTime(1000); - - const voteId = createdVoteId( - await voting.newVote(EMPTY_CALLS_SCRIPT, "metadata") - ); - await voting.mockIncreaseTime(1001); - voteState = await voting.getVote(voteId); - - assert.isFalse(voteState[0], "vote should be closed"); - assert.isFalse(voteState[1], "vote should not be executed"); - - await voting.unsafelyChangeVoteTime(1500); - voteState = await voting.getVote(voteId); - - assert.isTrue( - voteState[0], - "vote should be open after increasing of voting time" - ); - assert.isFalse(voteState[1], "vote should not be executed"); - }); - - it("close vote through changing of voting time", async () => { - await voting.unsafelyChangeVoteTime(1500); - - const voteId = createdVoteId( - await voting.newVote(EMPTY_CALLS_SCRIPT, "metadata") - ); - voteState = await voting.getVote(voteId); - - await voting.mockIncreaseTime(1001); - - assert.isTrue(voteState[0], "vote should be open"); - assert.isFalse(voteState[1], "vote should not be executed"); - - await voting.unsafelyChangeVoteTime(1); - voteState = await voting.getVote(voteId); - - assert.isFalse( - voteState[0], - "vote should be closed after time decreasing" - ); - assert.isFalse(voteState[1], "vote should not be executed"); - }); - - it("changing time does not affect executed votes", async () => { - await voting.unsafelyChangeVoteTime(1000); - - const voteId = createdVoteId( - await voting.newVote(EMPTY_CALLS_SCRIPT, "metadata") - ); - await voting.vote(voteId, true, false, { from: holder20 }); - await voting.vote(voteId, true, false, { from: holder29 }); - await voting.vote(voteId, true, false, { from: holder51 }); - await voting.mockIncreaseTime(1001 + objectionPhase); - await voting.executeVote(voteId); - - voteState = await voting.getVote(voteId); - - assert.isFalse(voteState[0], "vote should be closed after execution"); - assert.isTrue(voteState[1], "vite should be executed"); - - await voting.unsafelyChangeVoteTime(1500); - voteState = await voting.getVote(voteId); - - assert.isFalse( - voteState[0], - "vote should be closed after time increasing" - ); - assert.isTrue(voteState[1], "vite should be executed"); - }); - }); - - context("voting delegate", () => { - const neededSupport = pct16(50); - const minimumAcceptanceQuorum = pct16(20); - const decimals = 18; + context('empty token', () => { + const neededSupport = pct16(50) + const minimumAcceptanceQuorum = pct16(20) - beforeEach(async () => { - token = await MiniMeToken.new( - ZERO_ADDRESS, - ZERO_ADDRESS, - 0, - "n", - decimals, - "n", - true - ); // empty parameters minime - - await token.generateTokens(holder20, bigExp(20, decimals)); - await token.generateTokens(holder29, bigExp(29, decimals)); - await token.generateTokens(holder51, bigExp(51, decimals)); - await voting.initialize( - token.address, - neededSupport, - minimumAcceptanceQuorum, - votingDuration, - 0 - ); - - executionTarget = await ExecutionTarget.new(); - }); - - it("voter can set delegate", async () => { - const tx = await voting.setDelegate(delegate1, { from: holder29 }); - assertEvent(tx, "SetDelegate", { - expectedArgs: { voter: holder29, delegate: delegate1 }, - }); - assertAmountOfEvents(tx, "SetDelegate", { expectedAmount: 1 }); - - const delegate = await voting.getDelegate(holder29); - assert.equal( - delegate, - delegate1, - "holder29 should have delegate1 as a delegate" - ); - - const delegatedVoters = ( - await voting.getDelegatedVoters(delegate1, 0, 1) - )[0]; - assertArraysEqualAsSets( - delegatedVoters, - [holder29], - "delegate1 should be a delegate of holder29" - ); - }); - - it("voter can remove delegate", async () => { - await voting.setDelegate(delegate1, { from: holder29 }); - - const tx = await voting.removeDelegate({ from: holder29 }); - assertEvent(tx, "RemoveDelegate", { - expectedArgs: { voter: holder29, delegate: delegate1 }, - }); - assertAmountOfEvents(tx, "RemoveDelegate", { expectedAmount: 1 }); - const delegatedVoters = ( - await voting.getDelegatedVoters(delegate1, 0, 1) - )[0]; - assertArraysEqualAsSets( - delegatedVoters, - [], - "delegate1 should not be a delegate of anyone" - ); - }); - - it("voters can remove delegate", async () => { - await voting.setDelegate(delegate1, { from: holder20 }); - await voting.setDelegate(delegate1, { from: holder29 }); - await voting.setDelegate(delegate1, { from: holder51 }); - - const tx1 = await voting.removeDelegate({ from: holder29 }); - assertEvent(tx1, "RemoveDelegate", { - expectedArgs: { voter: holder29, delegate: delegate1 }, - }); - assertAmountOfEvents(tx1, "RemoveDelegate", { expectedAmount: 1 }); - const tx2 = await voting.removeDelegate({ from: holder51 }); - assertEvent(tx2, "RemoveDelegate", { - expectedArgs: { voter: holder51, delegate: delegate1 }, - }); - assertAmountOfEvents(tx2, "RemoveDelegate", { expectedAmount: 1 }); - const delegatedVoters = ( - await voting.getDelegatedVoters(delegate1, 0, 1) - )[0]; - assertArraysEqualAsSets( - delegatedVoters, - [holder20], - "delegate1 have only holder20 as a delegated voter" - ); - }); - - it("voter can change delegate", async () => { - await voting.setDelegate(delegate1, { from: holder29 }); - await voting.setDelegate(delegate2, { from: holder51 }); - - await voting.setDelegate(delegate2, { from: holder29 }); - - const delegatedVotersDelegate1 = ( - await voting.getDelegatedVoters(delegate1, 0, 1) - )[0]; - assertArraysEqualAsSets( - delegatedVotersDelegate1, - [], - "delegate1 should not be a delegate of anyone" - ); - const delegatedVotersDelegate2 = ( - await voting.getDelegatedVoters(delegate2, 0, 2) - )[0]; - assertArraysEqualAsSets( - delegatedVotersDelegate2, - [holder29, holder51], - "delegate2 should be a delegate of holder29 and holder51" - ); - }); - - it("delegate can manage several voters", async () => { - await voting.setDelegate(delegate1, { from: holder29 }); - - const tx = await voting.setDelegate(delegate1, { from: holder51 }); - assertEvent(tx, "SetDelegate", { - expectedArgs: { voter: holder51, delegate: delegate1 }, - }); - assertAmountOfEvents(tx, "SetDelegate", { expectedAmount: 1 }); - - const delegatedVoters = ( - await voting.getDelegatedVoters(delegate1, 0, 2) - )[0]; - assertArraysEqualAsSets( - delegatedVoters, - [holder29, holder51], - "delegate1 should be a delegate of holder29 and holder51" - ); - }); - }); - } -); + beforeEach(async() => { + token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', 0, 'n', true) // empty parameters minime + + await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, objectionPhase) + }) + + it('fails creating a vote if token has no holder', async () => { + await assertRevert(voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata'), ERRORS.VOTING_NO_VOTING_POWER) + }) + }) + + context('token supply = 1', () => { + const neededSupport = pct16(50) + const minimumAcceptanceQuorum = pct16(20) + + beforeEach(async () => { + token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', 0, 'n', true) // empty parameters minime + + await token.generateTokens(holder1, 1) + + await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, objectionPhase) + }) + + it('new vote cannot be executed before voting', async () => { + // Account creating vote does not have any tokens and therefore doesn't vote + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + assert.isFalse(await voting.canExecute(voteId), 'vote cannot be executed') + + await voting.vote(voteId, true, true, { from: holder1 }) + assert.isFalse(await voting.canExecute(voteId), 'vote cannot be executed') + + await voting.mockIncreaseTime(votingDuration + 1) + assert.isTrue(await voting.canExecute(voteId), 'vote may be executed') + + await voting.executeVote(voteId) + + const { open, executed } = await voting.getVote(voteId) + assert.isFalse(open, 'vote should be closed') + assert.isTrue(executed, 'vote should have been executed') + + }) + + context('new vote parameters', () => { + it('creating vote as holder does not execute vote (even if _canExecute param says so)', async () => { + const voteId = createdVoteId(await voting.methods['newVote(bytes,string,bool,bool)'](EMPTY_CALLS_SCRIPT, 'metadata', true, true, { from: holder1 })) + + const { open, executed } = await voting.getVote(voteId) + assert.isTrue(open, 'vote should be closed') + assert.isFalse(executed, 'vote should have been executed') + }) + + it("creating vote as holder doesn't execute vote if _canExecute param doesn't says so", async () => { + const voteId = createdVoteId(await voting.methods['newVote(bytes,string,bool,bool)'](EMPTY_CALLS_SCRIPT, 'metadata', true, false, { from: holder1 })) + + const { open, executed } = await voting.getVote(voteId) + assert.isTrue(open, 'vote should be open') + assert.isFalse(executed, 'vote should not have been executed') + }) + }) + }) + + context('token supply = 3', () => { + const neededSupport = pct16(34) + const minimumAcceptanceQuorum = pct16(20) + + beforeEach(async () => { + token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', 0, 'n', true) // empty parameters minime + + await token.generateTokens(holder1, 1) + await token.generateTokens(holder2, 2) + + await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, objectionPhase) + }) + + it('new vote cannot be executed before holder2 voting and time pass', async () => { + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + assert.isFalse(await voting.canExecute(voteId), 'vote cannot be executed') + assert.isFalse((await voting.getVote(voteId)).executed, 'vote should not have been executed') + + await voting.vote(voteId, true, true, { from: holder1 }) + assert.isFalse(await voting.canExecute(voteId), 'vote cannot be executed') + assert.isFalse((await voting.getVote(voteId)).executed, 'vote should not have been executed') + + await voting.vote(voteId, true, true, { from: holder2 }) + assert.isFalse(await voting.canExecute(voteId), 'vote cannot be executed') + assert.isFalse((await voting.getVote(voteId)).executed, 'vote should not have been executed') + + await voting.mockIncreaseTime(votingDuration + 1) + assert.isTrue(await voting.canExecute(voteId), 'vote may be executed') + + await voting.executeVote(voteId) + + const { open, executed } = await voting.getVote(voteId) + assert.isFalse(open, 'vote should be closed') + assert.isTrue(executed, 'vote should have been executed') + }) + + it('creating vote as holder2 does not execute vote', async () => { + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata', { from: holder2 })) + + const { open, executed } = await voting.getVote(voteId) + assert.isTrue(open, 'vote should be closed') + assert.isFalse(executed, 'vote should have been executed') + }) + }) + + context('changing token supply', () => { + const neededSupport = pct16(50) + const minimumAcceptanceQuorum = pct16(20) + + beforeEach(async () => { + token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', 0, 'n', true) // empty parameters minime + + await token.generateTokens(holder1, 1) + await token.generateTokens(holder2, 1) + + await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, objectionPhase) + }) + + it('uses the correct snapshot value if tokens are minted afterwards', async () => { + // Create vote and afterwards generate some tokens + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + await token.generateTokens(holder2, 1) + + const { snapshotBlock, votingPower } = await voting.getVote(voteId) + + // Generating tokens advanced the block by one + assertBn(snapshotBlock, await web3.eth.getBlockNumber() - 2, 'snapshot block should be correct') + assertBn(votingPower, await token.totalSupplyAt(snapshotBlock), 'voting power should match snapshot supply') + assertBn(votingPower, 2, 'voting power should be correct') + }) + + it('uses the correct snapshot value if tokens are minted in the same block', async () => { + // Create vote and generate some tokens in the same transaction + // Requires the voting mock to be the token's owner + await token.changeController(voting.address) + const voteId = createdVoteId(await voting.newTokenAndVote(holder2, 1, 'metadata')) + + const { snapshotBlock, votingPower } = await voting.getVote(voteId) + assertBn(snapshotBlock, await web3.eth.getBlockNumber() - 1, 'snapshot block should be correct') + assertBn(votingPower, await token.totalSupplyAt(snapshotBlock), 'voting power should match snapshot supply') + assertBn(votingPower, 2, 'voting power should be correct') + }) + }) + + context('before init', () => { + it('fails creating a vote before initialization', async () => { + await assertRevert(voting.newVote(encodeCallScript([]), ''), ERRORS.APP_AUTH_FAILED) + }) + + it('fails to forward actions before initialization', async () => { + const action = { to: executionTarget.address, calldata: executionTarget.contract.methods.execute().encodeABI() } + const script = encodeCallScript([action]) + await assertRevert(voting.forward(script, { from: holder51 }), ERRORS.VOTING_CAN_NOT_FORWARD) + }) + }) + + context('isValuePct unit test', async () => { + it('tests total = 0', async () => { + const result1 = await voting.isValuePct(0, 0, pct16(50)) + assert.equal(result1, false, "total 0 should always return false") + const result2 = await voting.isValuePct(1, 0, pct16(50)) + assert.equal(result2, false, "total 0 should always return false") + }) + + it('tests value = 0', async () => { + const result1 = await voting.isValuePct(0, 10, pct16(50)) + assert.equal(result1, false, "value 0 should false if pct is non-zero") + const result2 = await voting.isValuePct(0, 10, 0) + assert.equal(result2, false, "value 0 should return false if pct is zero") + }) + + it('tests pct ~= 100', async () => { + const result1 = await voting.isValuePct(10, 10, pct16(100).sub(bn(1))) + assert.equal(result1, true, "value 10 over 10 should pass") + }) + + it('tests strict inequality', async () => { + const result1 = await voting.isValuePct(10, 20, pct16(50)) + assert.equal(result1, false, "value 10 over 20 should not pass for 50%") + + const result2 = await voting.isValuePct(pct16(50).sub(bn(1)), pct16(100), pct16(50)) + assert.equal(result2, false, "off-by-one down should not pass") + + const result3 = await voting.isValuePct(pct16(50).add(bn(1)), pct16(100), pct16(50)) + assert.equal(result3, true, "off-by-one up should pass") + }) + }) + + context('unsafe time change', () => { + const neededSupport = pct16(1) + const minimumAcceptanceQuorum = pct16(1) + + beforeEach(async () => { + token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', 18, 'n', true) + await token.generateTokens(holder20, bigExp(20, 18)) + await token.generateTokens(holder29, bigExp(29, 18)) + await token.generateTokens(holder51, bigExp(51, 18)) + await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, 0) + executionTarget = await ExecutionTarget.new() + }) + + it('reverts on non-authorized vote time change request', async () => { + await aclP.revokePermission(ANY_ENTITY, voting.address, UNSAFELY_MODIFY_VOTE_TIME_ROLE, { from: root }) + + const entities = [nonHolder, holder1, holder29, holder51] + + for (ind = 0; ind < entities.length; ++ind) { + await assertRevert(voting.unsafelyChangeVoteTime(500, { from: entities[ind] }), 'APP_AUTH_FAILED') + } + }) + + it('simple change vote time', async () => { + const smallest = 1 + const increasingTime = 1500 + const decreasingTime = 500 + + // Allow to setting to smallest + receipt = await voting.unsafelyChangeVoteTime(smallest) + assertAmountOfEvents(receipt, 'ChangeVoteTime') + assert.equal(await voting.voteTime(), smallest, 'should have changed acceptance time') + + // Allow to increasing voteTime + receipt = await voting.unsafelyChangeVoteTime(increasingTime) + assertAmountOfEvents(receipt, 'ChangeVoteTime') + assert.equal(await voting.voteTime(), increasingTime, 'should have changed acceptance time') + + // Allow to decreasing voteTime + receipt = await voting.unsafelyChangeVoteTime(decreasingTime) + assertAmountOfEvents(receipt, 'ChangeVoteTime') + assert.equal(await voting.voteTime(), decreasingTime, 'should have changed acceptance time') + }) + + it('reverts on non-authorized obj time change request', async () => { + await aclP.revokePermission(ANY_ENTITY, voting.address, UNSAFELY_MODIFY_VOTE_TIME_ROLE, { from: root }) + + const entities = [nonHolder, holder1, holder29, holder51] + + for (ind = 0; ind < entities.length; ++ind) { + await assertRevert(voting.unsafelyChangeObjectionPhaseTime(100, { from: entities[ind] }), 'APP_AUTH_FAILED') + } + }) + + it('simple change obj time', async () => { + const increasingTime = 999 + const zeroTime = 0 + const decreasingTime = 500 + + // Allow to setting to zero + receipt = await voting.unsafelyChangeObjectionPhaseTime(increasingTime) + assertAmountOfEvents(receipt, 'ChangeObjectionPhaseTime') + assert.equal(await voting.objectionPhaseTime(), increasingTime, 'should have changed acceptance time') + + // Allow to increasing voteTime + receipt = await voting.unsafelyChangeObjectionPhaseTime(zeroTime) + assertAmountOfEvents(receipt, 'ChangeObjectionPhaseTime') + assert.equal(await voting.objectionPhaseTime(), zeroTime, 'should have changed acceptance time') + + // Allow to decreasing voteTime + receipt = await voting.unsafelyChangeObjectionPhaseTime(decreasingTime) + assertAmountOfEvents(receipt, 'ChangeObjectionPhaseTime') + assert.equal(await voting.objectionPhaseTime(), decreasingTime, 'should have changed acceptance time') + }) + + it('reverts if voteTime < objectionTime', async () => { + await assertRevert(voting.unsafelyChangeObjectionPhaseTime(votingDuration + 1), ERRORS.VOTING_OBJ_TIME_TOO_BIG) + + await voting.unsafelyChangeObjectionPhaseTime(votingDuration - 1) + + await assertRevert(voting.unsafelyChangeVoteTime(votingDuration - 1), ERRORS.VOTING_VOTE_TIME_TOO_SMALL) + await assertRevert(voting.unsafelyChangeVoteTime(0), ERRORS.VOTING_VOTE_TIME_TOO_SMALL) + }) + + it('re-open finished vote through changing of voting time', async () => { + await voting.unsafelyChangeVoteTime(1000) + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + await voting.mockIncreaseTime(1001) + voteState = await voting.getVote(voteId) + + assert.isFalse(voteState[0], 'vote should be closed') + assert.isFalse(voteState[1], 'vote should not be executed') + + await voting.unsafelyChangeVoteTime(1500) + voteState = await voting.getVote(voteId) + + assert.isTrue(voteState[0], 'vote should be open after increasing of voting time') + assert.isFalse(voteState[1], 'vote should not be executed') + }) + + it('close vote through changing of voting time', async () => { + await voting.unsafelyChangeVoteTime(1500) + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + voteState = await voting.getVote(voteId) + + await voting.mockIncreaseTime(1001) + + assert.isTrue(voteState[0], 'vote should be open') + assert.isFalse(voteState[1], 'vote should not be executed') + + await voting.unsafelyChangeVoteTime(1) + voteState = await voting.getVote(voteId) + + assert.isFalse(voteState[0], 'vote should be closed after time decreasing') + assert.isFalse(voteState[1], 'vote should not be executed') + }) + + it('changing time does not affect executed votes', async () => { + await voting.unsafelyChangeVoteTime(1000) + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + await voting.vote(voteId, true, false, { from: holder20 }) + await voting.vote(voteId, true, false, { from: holder29 }) + await voting.vote(voteId, true, false, { from: holder51 }) + await voting.mockIncreaseTime(1001 + objectionPhase) + await voting.executeVote(voteId) + + voteState = await voting.getVote(voteId) + + assert.isFalse(voteState[0], 'vote should be closed after execution') + assert.isTrue(voteState[1], 'vite should be executed') + + await voting.unsafelyChangeVoteTime(1500) + voteState = await voting.getVote(voteId) + + assert.isFalse(voteState[0], 'vote should be closed after time increasing') + assert.isTrue(voteState[1], 'vite should be executed') + }) + }) + + context('voting delegate', () => { + const neededSupport = pct16(50) + const minimumAcceptanceQuorum = pct16(20) + const decimals = 18 + + beforeEach(async () => { + token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', decimals, 'n', true) // empty parameters minime + + await token.generateTokens(holder20, bigExp(20, decimals)) + await token.generateTokens(holder29, bigExp(29, decimals)) + await token.generateTokens(holder51, bigExp(51, decimals)) + await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, 0) + + executionTarget = await ExecutionTarget.new() + }) + + it('voter can set delegate', async () => { + const tx = await voting.setDelegate(delegate1, {from: holder29}) + assertEvent(tx, 'SetDelegate', { + expectedArgs: {voter: holder29, delegate: delegate1} + }) + assertAmountOfEvents(tx, 'SetDelegate', {expectedAmount: 1}) + + const delegate = await voting.getDelegate(holder29) + assert.equal(delegate, delegate1, 'holder29 should have delegate1 as a delegate') + + const delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] + assertArraysEqualAsSets(delegatedVoters, [holder29], 'delegate1 should be a delegate of holder29') + }) + + it('voter can remove delegate', async () => { + await voting.setDelegate(delegate1, {from: holder29}) + + const tx = await voting.removeDelegate({from: holder29}) + assertEvent(tx, 'RemoveDelegate', { + expectedArgs: {voter: holder29, delegate: delegate1} + }) + assertAmountOfEvents(tx, 'RemoveDelegate', {expectedAmount: 1}) + const delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] + assertArraysEqualAsSets(delegatedVoters, [], 'delegate1 should not be a delegate of anyone') + }) + + it('voters can remove delegate', async () => { + await voting.setDelegate(delegate1, {from: holder20}) + await voting.setDelegate(delegate1, {from: holder29}) + await voting.setDelegate(delegate1, {from: holder51}) + + + const tx1 = await voting.removeDelegate({from: holder29}) + assertEvent(tx1, 'RemoveDelegate', { + expectedArgs: {voter: holder29, delegate: delegate1} + }) + assertAmountOfEvents(tx1, 'RemoveDelegate', {expectedAmount: 1}) + const tx2 = await voting.removeDelegate({from: holder51}) + assertEvent(tx2, 'RemoveDelegate', { + expectedArgs: {voter: holder51, delegate: delegate1} + }) + assertAmountOfEvents(tx2, 'RemoveDelegate', {expectedAmount: 1}) + const delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] + assertArraysEqualAsSets(delegatedVoters, [holder20], 'delegate1 have only holder20 as a delegated voter') + }) + + it('voter can change delegate', async () => { + await voting.setDelegate(delegate1, {from: holder29}) + await voting.setDelegate(delegate2, {from: holder51}) + + await voting.setDelegate(delegate2, {from: holder29}) + + const delegatedVotersDelegate1 = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] + assertArraysEqualAsSets(delegatedVotersDelegate1, [], 'delegate1 should not be a delegate of anyone') + const delegatedVotersDelegate2 = (await voting.getDelegatedVoters(delegate2, 0, 2))[0] + assertArraysEqualAsSets(delegatedVotersDelegate2, [holder29, holder51], 'delegate2 should be a delegate of holder29 and holder51') + }) + + it('delegate can manage several voters', async () => { + await voting.setDelegate(delegate1, {from: holder29}) + + const tx = await voting.setDelegate(delegate1, {from: holder51}) + assertEvent(tx, 'SetDelegate', { + expectedArgs: {voter: holder51, delegate: delegate1} + }) + assertAmountOfEvents(tx, 'SetDelegate', {expectedAmount: 1}) + + const delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 2))[0] + assertArraysEqualAsSets(delegatedVoters, [holder29, holder51], 'delegate1 should be a delegate of holder29 and holder51') + }) + }) +}) \ No newline at end of file From 4d69635661374713526d52438afdf5c4a9bac1f0 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Mon, 4 Mar 2024 10:27:44 +0100 Subject: [PATCH 033/100] style: fix errors code style --- apps/voting/test/helpers/errors.js | 50 +++++++++++++++--------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/apps/voting/test/helpers/errors.js b/apps/voting/test/helpers/errors.js index 3c3751f8d..0f4223334 100644 --- a/apps/voting/test/helpers/errors.js +++ b/apps/voting/test/helpers/errors.js @@ -1,32 +1,32 @@ -const { makeErrorMappingProxy } = require("@aragon/contract-helpers-test"); +const { makeErrorMappingProxy } = require('@aragon/contract-helpers-test'); module.exports = makeErrorMappingProxy({ // aragonOS errors - APP_AUTH_FAILED: "APP_AUTH_FAILED", - INIT_ALREADY_INITIALIZED: "INIT_ALREADY_INITIALIZED", - INIT_NOT_INITIALIZED: "INIT_NOT_INITIALIZED", - RECOVER_DISALLOWED: "RECOVER_DISALLOWED", + APP_AUTH_FAILED: 'APP_AUTH_FAILED', + INIT_ALREADY_INITIALIZED: 'INIT_ALREADY_INITIALIZED', + INIT_NOT_INITIALIZED: 'INIT_NOT_INITIALIZED', + RECOVER_DISALLOWED: 'RECOVER_DISALLOWED', // Voting errors - VOTING_NO_VOTE: "VOTING_NO_VOTE", - VOTING_INIT_PCTS: "VOTING_INIT_PCTS", - VOTING_CHANGE_SUPPORT_PCTS: "VOTING_CHANGE_SUPPORT_PCTS", - VOTING_CHANGE_QUORUM_PCTS: "VOTING_CHANGE_QUORUM_PCTS", - VOTING_INIT_SUPPORT_TOO_BIG: "VOTING_INIT_SUPPORT_TOO_BIG", - VOTING_CHANGE_SUPP_TOO_BIG: "VOTING_CHANGE_SUPP_TOO_BIG", - VOTING_CAN_NOT_VOTE: "VOTING_CAN_NOT_VOTE", - VOTING_CAN_NOT_EXECUTE: "VOTING_CAN_NOT_EXECUTE", - VOTING_CAN_NOT_FORWARD: "VOTING_CAN_NOT_FORWARD", - VOTING_NO_VOTING_POWER: "VOTING_NO_VOTING_POWER", - VOTING_OBJ_TIME_TOO_BIG: "VOTING_OBJ_TIME_TOO_BIG", - VOTING_VOTE_TIME_TOO_SMALL: "VOTING_VOTE_TIME_TOO_SMALL", - VOTING_INIT_OBJ_TIME_TOO_BIG: "VOTING_INIT_OBJ_TIME_TOO_BIG", + VOTING_NO_VOTE: 'VOTING_NO_VOTE', + VOTING_INIT_PCTS: 'VOTING_INIT_PCTS', + VOTING_CHANGE_SUPPORT_PCTS: 'VOTING_CHANGE_SUPPORT_PCTS', + VOTING_CHANGE_QUORUM_PCTS: 'VOTING_CHANGE_QUORUM_PCTS', + VOTING_INIT_SUPPORT_TOO_BIG: 'VOTING_INIT_SUPPORT_TOO_BIG', + VOTING_CHANGE_SUPP_TOO_BIG: 'VOTING_CHANGE_SUPP_TOO_BIG', + VOTING_CAN_NOT_VOTE: 'VOTING_CAN_NOT_VOTE', + VOTING_CAN_NOT_EXECUTE: 'VOTING_CAN_NOT_EXECUTE', + VOTING_CAN_NOT_FORWARD: 'VOTING_CAN_NOT_FORWARD', + VOTING_NO_VOTING_POWER: 'VOTING_NO_VOTING_POWER', + VOTING_OBJ_TIME_TOO_BIG: 'VOTING_OBJ_TIME_TOO_BIG', + VOTING_VOTE_TIME_TOO_SMALL: 'VOTING_VOTE_TIME_TOO_SMALL', + VOTING_INIT_OBJ_TIME_TOO_BIG: 'VOTING_INIT_OBJ_TIME_TOO_BIG', - // Voting manager errors - VOTING_CAN_NOT_VOTE_FOR: "VOTING_CAN_NOT_VOTE_FOR", - VOTING_ZERO_ADDRESS_PASSED: "VOTING_ZERO_ADDRESS_PASSED", - VOTING_DELEGATE_NOT_SET: "VOTING_DELEGATE_NOT_SET", - VOTING_SELF_DELEGATE: "VOTING_SELF_DELEGATE", - VOTING_DELEGATE_SAME_AS_PREV: "VOTING_DELEGATE_SAME_AS_PREV", - VOTING_DELEGATE_CANT_OVERWRITE: "VOTING_DELEGATE_CANT_OVERWRITE", + // Delegation errors + VOTING_CAN_NOT_VOTE_FOR: 'VOTING_CAN_NOT_VOTE_FOR', + VOTING_ZERO_ADDRESS_PASSED: 'VOTING_ZERO_ADDRESS_PASSED', + VOTING_DELEGATE_NOT_SET: 'VOTING_DELEGATE_NOT_SET', + VOTING_SELF_DELEGATE: 'VOTING_SELF_DELEGATE', + VOTING_DELEGATE_SAME_AS_PREV: 'VOTING_DELEGATE_SAME_AS_PREV', + VOTING_DELEGATE_CANT_OVERWRITE: 'VOTING_DELEGATE_CANT_OVERWRITE', }); From 82e9a335048e174e9a5bcd5f2ceaad9c188697b8 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Mon, 4 Mar 2024 10:29:44 +0100 Subject: [PATCH 034/100] style: remove semicolons from errors file --- apps/voting/test/helpers/errors.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/voting/test/helpers/errors.js b/apps/voting/test/helpers/errors.js index 0f4223334..1de6e6b1b 100644 --- a/apps/voting/test/helpers/errors.js +++ b/apps/voting/test/helpers/errors.js @@ -1,4 +1,4 @@ -const { makeErrorMappingProxy } = require('@aragon/contract-helpers-test'); +const { makeErrorMappingProxy } = require('@aragon/contract-helpers-test') module.exports = makeErrorMappingProxy({ // aragonOS errors @@ -29,4 +29,4 @@ module.exports = makeErrorMappingProxy({ VOTING_SELF_DELEGATE: 'VOTING_SELF_DELEGATE', VOTING_DELEGATE_SAME_AS_PREV: 'VOTING_DELEGATE_SAME_AS_PREV', VOTING_DELEGATE_CANT_OVERWRITE: 'VOTING_DELEGATE_CANT_OVERWRITE', -}); +}) From 678626b9a23820ceb540fab9eeeec4f34042141d Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Mon, 4 Mar 2024 11:44:03 +0100 Subject: [PATCH 035/100] refactor: address reviewers rfc --- apps/voting/contracts/Voting.sol | 89 +++++++++++++++----------------- 1 file changed, 42 insertions(+), 47 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index a711cfd39..3a12ee6f3 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -224,8 +224,9 @@ contract Voting is IForwarder, AragonApp { * @param _executesIfDecided_deprecated Whether the vote should execute its action if it becomes decided */ function vote(uint256 _voteId, bool _supports, bool _executesIfDecided_deprecated) external voteExists(_voteId) { - require(_canParticipateInVote(_voteId, _supports), ERROR_CAN_NOT_VOTE); - require(_hasVotingPower(votes[_voteId], msg.sender), ERROR_NO_VOTING_POWER); + Vote storage vote_ = votes[_voteId]; + require(_canParticipateInVote(vote_, _supports), ERROR_CAN_NOT_VOTE); + require(_hasVotingPower(vote_, msg.sender), ERROR_NO_VOTING_POWER); _vote(_voteId, _supports, msg.sender, false); } @@ -243,35 +244,32 @@ contract Voting is IForwarder, AragonApp { * @notice Assign `_delegate` as the delegate for the sender * @param _delegate address to delegate to */ - function setDelegate(address _delegate) public { + function setDelegate(address _delegate) external { require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); + require(_delegate != msg.sender, ERROR_SELF_DELEGATE); - address msgSender = msg.sender; - require(_delegate != msgSender, ERROR_SELF_DELEGATE); - - address prevDelegate = delegates[msgSender].delegate; + address prevDelegate = delegates[msg.sender].delegate; require(_delegate != prevDelegate, ERROR_DELEGATE_SAME_AS_PREV); - uint256 votingPower = token.balanceOfAt(msgSender, getBlockNumber64() - 1); + uint256 votingPower = token.balanceOfAt(msg.sender, getBlockNumber64() - 1); require(votingPower > 0, ERROR_NO_VOTING_POWER); if (prevDelegate != address(0)) { - _removeDelegatedAddressFor(prevDelegate, msgSender); + _removeDelegatedAddressFor(prevDelegate, msg.sender); } - _addDelegatedAddressFor(_delegate, msgSender); + _addDelegatedAddressFor(_delegate, msg.sender); - emit SetDelegate(msgSender, _delegate); + emit SetDelegate(msg.sender, _delegate); } - function removeDelegate() public { - address msgSender = msg.sender; - address prevDelegate = delegates[msgSender].delegate; + function removeDelegate() external { + address prevDelegate = delegates[msg.sender].delegate; require(prevDelegate != address(0), ERROR_DELEGATE_NOT_SET); - _removeDelegatedAddressFor(prevDelegate, msgSender); - delete delegates[msgSender]; + _removeDelegatedAddressFor(prevDelegate, msg.sender); + delete delegates[msg.sender]; - emit RemoveDelegate(msgSender, prevDelegate); + emit RemoveDelegate(msg.sender, prevDelegate); } /** @@ -281,15 +279,13 @@ contract Voting is IForwarder, AragonApp { * @param _voters list of voters */ function voteForMultiple(uint256 _voteId, bool _supports, address[] _voters) public voteExists(_voteId) { - require(_canParticipateInVote(_voteId, _supports), ERROR_CAN_NOT_VOTE); - Vote storage vote_ = votes[_voteId]; - address msgSender = msg.sender; + require(_canParticipateInVote(vote_, _supports), ERROR_CAN_NOT_VOTE); for (uint256 i = 0; i < _voters.length; ++i) { address voter = _voters[i]; require(_hasVotingPower(vote_, voter), ERROR_NO_VOTING_POWER); - require(_canVoteFor(vote_, msgSender, voter), ERROR_CAN_NOT_VOTE_FOR); + require(_canVoteFor(vote_, msg.sender, voter), ERROR_CAN_NOT_VOTE_FOR); _vote(_voteId, _supports, voter, true); } } @@ -361,7 +357,7 @@ contract Voting is IForwarder, AragonApp { */ function canVote(uint256 _voteId, address _voter) external view voteExists(_voteId) returns (bool) { Vote storage vote_ = votes[_voteId]; - return _canParticipateInVote(_voteId, false) && _hasVotingPower(vote_, _voter); + return _canParticipateInVote(vote_, false) && _hasVotingPower(vote_, _voter); } /** @@ -429,7 +425,7 @@ contract Voting is IForwarder, AragonApp { * @param _voter address of the voter * @return VoterState of the requested voter for a certain vote */ - function getVoterState(uint256 _voteId, address _voter) public view voteExists(_voteId) returns (VoterState) { + function getVoterState(uint256 _voteId, address _voter) external view voteExists(_voteId) returns (VoterState) { return votes[_voteId].voters[_voter]; } @@ -441,7 +437,7 @@ contract Voting is IForwarder, AragonApp { * @return the array of delegated voters * @return the array of voting power of delegated voters */ - function getDelegatedVoters(address _delegate, uint256 _offset, uint256 _limit) public view returns (address[] memory, uint256[] memory) { + function getDelegatedVoters(address _delegate, uint256 _offset, uint256 _limit) external view returns (address[] memory, uint256[] memory) { return _getDelegatedVotersAt(_delegate, _offset, _limit, getBlockNumber64()); } @@ -453,7 +449,7 @@ contract Voting is IForwarder, AragonApp { * @return the array of delegated voters * @return the array of voting power of delegated voters */ - function getDelegatedVotersAtVote(address _delegate, uint256 _offset, uint256 _limit, uint256 _voteId) public view voteExists(_voteId) returns (address[] memory, uint256[] memory) { + function getDelegatedVotersAtVote(address _delegate, uint256 _offset, uint256 _limit, uint256 _voteId) external view voteExists(_voteId) returns (address[] memory, uint256[] memory) { Vote storage vote_ = votes[_voteId]; return _getDelegatedVotersAt(_delegate, _offset, _limit, vote_.snapshotBlock); } @@ -463,7 +459,7 @@ contract Voting is IForwarder, AragonApp { * @param _delegate address of the delegate * @return the number of delegated voters */ - function getDelegatedVotersCount(address _delegate) public view returns (uint256) { + function getDelegatedVotersCount(address _delegate) external view returns (uint256) { require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); return delegatedVoters[_delegate].addresses.length; } @@ -472,7 +468,7 @@ contract Voting is IForwarder, AragonApp { * @notice Return the delegate address assigned to the `_voter` * @param _voter the address of the voter */ - function getDelegate(address _voter) public view returns (address) { + function getDelegate(address _voter) external view returns (address) { require(_voter != address(0), ERROR_ZERO_ADDRESS_PASSED); return delegates[_voter].delegate; } @@ -482,14 +478,13 @@ contract Voting is IForwarder, AragonApp { * @param _voters the list of voters * @param _voteId Vote identifier */ - function getVotersStateAtVote(address[] _voters, uint256 _voteId) public view voteExists(_voteId) returns (VoterState[] memory voterStatesList) { - uint256 length = _voters.length; - voterStatesList = new VoterState[](length); + function getVotersStateAtVote(uint256 _voteId, address[] _voters) external view voteExists(_voteId) returns (VoterState[] memory voterStatesList) { + uint256 votersCount = _voters.length; + voterStatesList = new VoterState[](votersCount); Vote storage vote_ = votes[_voteId]; - for (uint256 i = 0; i < length; ++i) { + for (uint256 i = 0; i < votersCount; ++i) { voterStatesList[i] = vote_.voters[_voters[i]]; } - return voterStatesList; } // Internal fns @@ -581,11 +576,11 @@ contract Voting is IForwarder, AragonApp { * @param _voter address of the voter */ function _addDelegatedAddressFor(address _delegate, address _voter) internal { - uint256 length = delegatedVoters[_delegate].addresses.length; - require(length < UINT_96_MAX, ERROR_MAX_DELEGATED_VOTERS_REACHED); + uint256 delegatedVotersCount = delegatedVoters[_delegate].addresses.length; + require(delegatedVotersCount <= UINT_96_MAX, ERROR_MAX_DELEGATED_VOTERS_REACHED); delegatedVoters[_delegate].addresses.push(_voter); - delegates[_voter] = Delegate(_delegate, uint96(delegatedVoters[_delegate].addresses.length.sub(1))); + delegates[_voter] = Delegate(_delegate, uint96(delegatedVotersCount)); } /** @@ -594,12 +589,13 @@ contract Voting is IForwarder, AragonApp { * @param _voter address of the voter */ function _removeDelegatedAddressFor(address _delegate, address _voter) internal { - uint256 length = delegatedVoters[_delegate].addresses.length; - require(length > 0, ERROR_DELEGATE_NOT_SET); + uint256 delegatedVotersCount = delegatedVoters[_delegate].addresses.length; + require(delegatedVotersCount > 0, ERROR_DELEGATE_NOT_SET); uint96 voterIndex = delegates[_voter].voterIndex; - address lastVoter = delegatedVoters[_delegate].addresses[length - 1]; - if (voterIndex < length - 1) { + assert(delegatedVoters[_delegate].addresses[voterIndex] == _voter); + address lastVoter = delegatedVoters[_delegate].addresses[delegatedVotersCount - 1]; + if (voterIndex < delegatedVotersCount - 1) { delegatedVoters[_delegate].addresses[voterIndex] = lastVoter; delegates[lastVoter].voterIndex = voterIndex; } @@ -617,13 +613,13 @@ contract Voting is IForwarder, AragonApp { function _getDelegatedVotersAt(address _delegate, uint256 _offset, uint256 _limit, uint256 _blockNumber) internal view returns (address[] memory votersList, uint256[] memory votingPowerList) { require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); require(_limit > 0, ERROR_INVALID_LIMIT); - uint256 length = delegatedVoters[_delegate].addresses.length; - if (length == 0) { - return (new address[](0), new uint256[](0)); + uint256 delegatedVotersCount = delegatedVoters[_delegate].addresses.length; + if (delegatedVotersCount == 0) { + return (votersList, votingPowerList); } - require(_offset < length, ERROR_INVALID_OFFSET); + require(_offset < delegatedVotersCount, ERROR_INVALID_OFFSET); - uint256 returnCount = _offset.add(_limit) > length ? length.sub(_offset) : _limit; + uint256 returnCount = _offset.add(_limit) > delegatedVotersCount ? delegatedVotersCount.sub(_offset) : _limit; votersList = new address[](returnCount); votingPowerList = new uint256[](returnCount); for (uint256 i = 0; i < returnCount; ++i) { @@ -668,9 +664,8 @@ contract Voting is IForwarder, AragonApp { * @dev Internal function to check if a voter can participate on a vote. It assumes the queried vote exists. * @return True if the given voter can participate a certain vote, false otherwise */ - function _canParticipateInVote(uint256 _voteId, bool _supports) internal view returns (bool) { - Vote storage vote_ = votes[_voteId]; - return _isVoteOpen(vote_) && _isValidPhaseToVote(vote_, _supports); + function _canParticipateInVote(Vote storage _vote, bool _supports) internal view returns (bool) { + return _isVoteOpen(_vote) && _isValidPhaseToVote(_vote, _supports); } function _isDelegateFor(address _delegate, address _voter) internal view returns (bool) { From 198e34375e3f7e74c0ff8aefade600e98abdc1d2 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Mon, 4 Mar 2024 11:54:26 +0100 Subject: [PATCH 036/100] refactor: merge _isValidPhaseToVote and _canParticipateInVote functions, rename _canParticipateInVote to _isValidPhaseToVote --- apps/voting/contracts/Voting.sol | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 3a12ee6f3..032933b88 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -225,7 +225,7 @@ contract Voting is IForwarder, AragonApp { */ function vote(uint256 _voteId, bool _supports, bool _executesIfDecided_deprecated) external voteExists(_voteId) { Vote storage vote_ = votes[_voteId]; - require(_canParticipateInVote(vote_, _supports), ERROR_CAN_NOT_VOTE); + require(_isValidPhaseToVote(vote_, _supports), ERROR_CAN_NOT_VOTE); require(_hasVotingPower(vote_, msg.sender), ERROR_NO_VOTING_POWER); _vote(_voteId, _supports, msg.sender, false); } @@ -280,7 +280,7 @@ contract Voting is IForwarder, AragonApp { */ function voteForMultiple(uint256 _voteId, bool _supports, address[] _voters) public voteExists(_voteId) { Vote storage vote_ = votes[_voteId]; - require(_canParticipateInVote(vote_, _supports), ERROR_CAN_NOT_VOTE); + require(_isValidPhaseToVote(vote_, _supports), ERROR_CAN_NOT_VOTE); for (uint256 i = 0; i < _voters.length; ++i) { address voter = _voters[i]; @@ -357,7 +357,7 @@ contract Voting is IForwarder, AragonApp { */ function canVote(uint256 _voteId, address _voter) external view voteExists(_voteId) returns (bool) { Vote storage vote_ = votes[_voteId]; - return _canParticipateInVote(vote_, false) && _hasVotingPower(vote_, _voter); + return _isVoteOpen(vote_) && _hasVotingPower(vote_, _voter); } /** @@ -664,8 +664,8 @@ contract Voting is IForwarder, AragonApp { * @dev Internal function to check if a voter can participate on a vote. It assumes the queried vote exists. * @return True if the given voter can participate a certain vote, false otherwise */ - function _canParticipateInVote(Vote storage _vote, bool _supports) internal view returns (bool) { - return _isVoteOpen(_vote) && _isValidPhaseToVote(_vote, _supports); + function _isValidPhaseToVote(Vote storage _vote, bool _supports) internal view returns (bool) { + return _isVoteOpen(_vote) && (!_supports || _getVotePhase(_vote) == VotePhase.Main); } function _isDelegateFor(address _delegate, address _voter) internal view returns (bool) { @@ -679,10 +679,6 @@ contract Voting is IForwarder, AragonApp { return _isDelegateFor(_delegate, _voter) && !_hasVoted(_vote, _voter); } - function _isValidPhaseToVote(Vote storage _vote, bool _supports) internal view returns (bool) { - return !_supports || _getVotePhase(_vote) == VotePhase.Main; - } - function _hasVoted(Vote storage _vote, address _voter) internal view returns (bool) { VoterState state = _vote.voters[_voter]; return state == VoterState.Yea || state == VoterState.Nay; From 243abe1648c0c6037af7ccdc3ea6485f2d4ee667 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Mon, 4 Mar 2024 12:03:01 +0100 Subject: [PATCH 037/100] refactor: rename removeDelegate to resetDelegate; move Set/ResetDelegate event emit to internal methods --- apps/voting/contracts/Voting.sol | 13 +++++++------ apps/voting/test/voting.js | 18 +++++++++--------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 032933b88..09bb6a3bb 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -97,7 +97,7 @@ contract Voting is IForwarder, AragonApp { event ChangeVoteTime(uint64 voteTime); event ChangeObjectionPhaseTime(uint64 objectionPhaseTime); event SetDelegate(address indexed voter, address indexed delegate); - event RemoveDelegate(address indexed voter, address indexed delegate); + event ResetDelegate(address indexed voter, address indexed delegate); event CastVoteAsDelegate(uint256 indexed voteId, address indexed delegate, address indexed voter, bool supports, uint256 stake); modifier voteExists(uint256 _voteId) { @@ -258,18 +258,17 @@ contract Voting is IForwarder, AragonApp { _removeDelegatedAddressFor(prevDelegate, msg.sender); } _addDelegatedAddressFor(_delegate, msg.sender); - - emit SetDelegate(msg.sender, _delegate); } - function removeDelegate() external { + /** + * @notice Unassign `_delegate` from the sender + */ + function resetDelegate() external { address prevDelegate = delegates[msg.sender].delegate; require(prevDelegate != address(0), ERROR_DELEGATE_NOT_SET); _removeDelegatedAddressFor(prevDelegate, msg.sender); delete delegates[msg.sender]; - - emit RemoveDelegate(msg.sender, prevDelegate); } /** @@ -581,6 +580,7 @@ contract Voting is IForwarder, AragonApp { delegatedVoters[_delegate].addresses.push(_voter); delegates[_voter] = Delegate(_delegate, uint96(delegatedVotersCount)); + emit SetDelegate(_voter, _delegate); } /** @@ -600,6 +600,7 @@ contract Voting is IForwarder, AragonApp { delegates[lastVoter].voterIndex = voterIndex; } delegatedVoters[_delegate].addresses.length--; + emit ResetDelegate(_voter, _delegate); } /** diff --git a/apps/voting/test/voting.js b/apps/voting/test/voting.js index 92d791635..adf179942 100644 --- a/apps/voting/test/voting.js +++ b/apps/voting/test/voting.js @@ -930,11 +930,11 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d it('voter can remove delegate', async () => { await voting.setDelegate(delegate1, {from: holder29}) - const tx = await voting.removeDelegate({from: holder29}) - assertEvent(tx, 'RemoveDelegate', { + const tx = await voting.resetDelegate({from: holder29}) + assertEvent(tx, 'ResetDelegate', { expectedArgs: {voter: holder29, delegate: delegate1} }) - assertAmountOfEvents(tx, 'RemoveDelegate', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'ResetDelegate', {expectedAmount: 1}) const delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] assertArraysEqualAsSets(delegatedVoters, [], 'delegate1 should not be a delegate of anyone') }) @@ -945,16 +945,16 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d await voting.setDelegate(delegate1, {from: holder51}) - const tx1 = await voting.removeDelegate({from: holder29}) - assertEvent(tx1, 'RemoveDelegate', { + const tx1 = await voting.resetDelegate({from: holder29}) + assertEvent(tx1, 'ResetDelegate', { expectedArgs: {voter: holder29, delegate: delegate1} }) - assertAmountOfEvents(tx1, 'RemoveDelegate', {expectedAmount: 1}) - const tx2 = await voting.removeDelegate({from: holder51}) - assertEvent(tx2, 'RemoveDelegate', { + assertAmountOfEvents(tx1, 'ResetDelegate', {expectedAmount: 1}) + const tx2 = await voting.resetDelegate({from: holder51}) + assertEvent(tx2, 'ResetDelegate', { expectedArgs: {voter: holder51, delegate: delegate1} }) - assertAmountOfEvents(tx2, 'RemoveDelegate', {expectedAmount: 1}) + assertAmountOfEvents(tx2, 'ResetDelegate', {expectedAmount: 1}) const delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] assertArraysEqualAsSets(delegatedVoters, [holder20], 'delegate1 have only holder20 as a delegated voter') }) From 208702c64f0a8391540457c00ab38ffe656c5a97 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Mon, 4 Mar 2024 12:11:19 +0100 Subject: [PATCH 038/100] refactor: rename _hasVoted to _hasVotedDirectly --- apps/voting/contracts/Voting.sol | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 09bb6a3bb..1750fcfd5 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -662,7 +662,8 @@ contract Voting is IForwarder, AragonApp { } /** - * @dev Internal function to check if a voter can participate on a vote. It assumes the queried vote exists. + * @dev Internal function to check if the vote is open and given option is applicable at the current phase. + * It assumes the queried vote exists. * @return True if the given voter can participate a certain vote, false otherwise */ function _isValidPhaseToVote(Vote storage _vote, bool _supports) internal view returns (bool) { @@ -677,10 +678,10 @@ contract Voting is IForwarder, AragonApp { } function _canVoteFor(Vote storage _vote, address _delegate, address _voter) internal view returns (bool) { - return _isDelegateFor(_delegate, _voter) && !_hasVoted(_vote, _voter); + return _isDelegateFor(_delegate, _voter) && !_hasVotedDirectly(_vote, _voter); } - function _hasVoted(Vote storage _vote, address _voter) internal view returns (bool) { + function _hasVotedDirectly(Vote storage _vote, address _voter) internal view returns (bool) { VoterState state = _vote.voters[_voter]; return state == VoterState.Yea || state == VoterState.Nay; } From 997dd7e03f85b19a411b2ff721edc018563a1857 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Mon, 4 Mar 2024 12:32:26 +0100 Subject: [PATCH 039/100] refactor: no revert for `_canVoteFor` in `voteForMultiple`, rename `voteFor` to `attemptVoteFor` (#40) * refactor: do not revert if delegated voter has voted before the delegate * refactor: rename voteFor to attemptVoteFor, rename voteForMultiple to attemptVoteForMultiple --- apps/voting/contracts/Voting.sol | 15 ++++++++++----- apps/voting/test/voting.js | 22 +++++++++++++++------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 1750fcfd5..72b5bc752 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -277,16 +277,21 @@ contract Voting is IForwarder, AragonApp { * @param _supports Whether the delegate supports the vote * @param _voters list of voters */ - function voteForMultiple(uint256 _voteId, bool _supports, address[] _voters) public voteExists(_voteId) { + function attemptVoteForMultiple(uint256 _voteId, bool _supports, address[] _voters) public voteExists(_voteId) { Vote storage vote_ = votes[_voteId]; require(_isValidPhaseToVote(vote_, _supports), ERROR_CAN_NOT_VOTE); + bool hasManagedToVote = false; for (uint256 i = 0; i < _voters.length; ++i) { address voter = _voters[i]; require(_hasVotingPower(vote_, voter), ERROR_NO_VOTING_POWER); - require(_canVoteFor(vote_, msg.sender, voter), ERROR_CAN_NOT_VOTE_FOR); - _vote(_voteId, _supports, voter, true); + if (_canVoteFor(vote_, msg.sender, voter)) { + _vote(_voteId, _supports, voter, true); + hasManagedToVote = true; + } } + + require(hasManagedToVote, ERROR_CAN_NOT_VOTE_FOR); } /** @@ -295,10 +300,10 @@ contract Voting is IForwarder, AragonApp { * @param _supports Whether the delegate supports the vote * @param _voter address of the voter */ - function voteFor(uint256 _voteId, bool _supports, address _voter) external voteExists(_voteId) { + function attemptVoteFor(uint256 _voteId, bool _supports, address _voter) external voteExists(_voteId) { address[] memory voters = new address[](1); voters[0] = _voter; - voteForMultiple(_voteId, _supports, voters); + attemptVoteForMultiple(_voteId, _supports, voters); } // Forwarding fns diff --git a/apps/voting/test/voting.js b/apps/voting/test/voting.js index adf179942..0182dc0c9 100644 --- a/apps/voting/test/voting.js +++ b/apps/voting/test/voting.js @@ -439,7 +439,7 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d it('delegate can vote for voter', async () => { - const tx = await voting.voteFor(voteId, false, holder29, {from: delegate1}) + const tx = await voting.attemptVoteFor(voteId, false, holder29, {from: delegate1}) assertEvent(tx, 'CastVote', {expectedArgs: {voteId: voteId, voter: holder29, supports: false}}) assertEvent(tx, 'CastVoteAsDelegate', {expectedArgs: {voteId: voteId, delegate: delegate1, voter: holder29, supports: false}}) assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) @@ -454,7 +454,7 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d }) it('delegate can vote for both voters', async () => { - const tx = await voting.voteForMultiple(voteId, false, [holder29, holder51], {from: delegate1}) + const tx = await voting.attemptVoteForMultiple(voteId, false, [holder29, holder51], {from: delegate1}) assertEvent(tx, 'CastVote', {expectedArgs: {voteId: voteId, voter: holder29, supports: false}}) assertEvent(tx, 'CastVote', {index: 1, expectedArgs: {voteId: voteId, voter: holder51, supports: false}}) assertEvent(tx, 'CastVoteAsDelegate', {expectedArgs: {voteId: voteId, delegate: delegate1, voter: holder29, supports: false}}) @@ -473,16 +473,24 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d assert.equal(voterState51, VOTER_STATE.DELEGATE_NAY, 'holder51 should have delegate nay voter status') }) - it(`delegate can't vote for both voters if one has previously voted`, async () => { + it(`revert if both voters has voted before`, async () => { await voting.vote(voteId, false, true, { from: holder29 }) + await voting.vote(voteId, false, true, { from: holder51 }) await assertRevert( - voting.voteForMultiple(voteId, false, [holder29, holder51], {from: delegate1}), + voting.attemptVoteForMultiple(voteId, false, [holder29, holder51], {from: delegate1}), ERRORS.VOTING_CAN_NOT_VOTE_FOR ) }) + it(`delegate can vote for one of the two if the other one has voted before`, async () => { + await voting.vote(voteId, false, true, { from: holder51 }) + const tx = await voting.attemptVoteForMultiple(voteId, false, [holder29, holder51], {from: delegate1}) + assertEvent(tx, 'CastVoteAsDelegate', {expectedArgs: {voteId: voteId, delegate: delegate1, voter: holder29, supports: false}}) + assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 1}) + }) + it(`voter can overwrite delegate's vote`, async () => { - await voting.voteFor(voteId, false, holder29, {from: delegate1}) + await voting.attemptVoteFor(voteId, false, holder29, {from: delegate1}) const tx = await voting.vote(voteId, true, true, {from: holder29}) assertEvent(tx, 'CastVote', {expectedArgs: {voteId: voteId, voter: holder29, supports: true}}) @@ -498,11 +506,11 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d }) it(`delegate can't overwrite voter's vote`, async () => { - await voting.voteFor(voteId, false, holder29, {from: delegate1}) + await voting.attemptVoteFor(voteId, false, holder29, {from: delegate1}) await voting.vote(voteId, true, true, {from: holder29}) await assertRevert( - voting.voteFor( + voting.attemptVoteFor( voteId, false, holder29, From 08d43e3287051db51049b5703d97ddd9b02afaff Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Fri, 15 Mar 2024 23:42:12 +0700 Subject: [PATCH 040/100] fix: audit fixes --- apps/voting/contracts/Voting.sol | 46 +++++++++++++++++++++++--------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 72b5bc752..f1257818f 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -35,8 +35,8 @@ contract Voting is IForwarder, AragonApp { string private constant ERROR_CAN_NOT_EXECUTE = "VOTING_CAN_NOT_EXECUTE"; string private constant ERROR_CAN_NOT_FORWARD = "VOTING_CAN_NOT_FORWARD"; string private constant ERROR_NO_VOTING_POWER = "VOTING_NO_VOTING_POWER"; - string private constant ERROR_CHANGE_VOTE_TIME = "VOTING_VOTE_TIME_TOO_SMALL"; - string private constant ERROR_CHANGE_OBJECTION_TIME = "VOTING_OBJ_TIME_TOO_BIG"; + string private constant ERROR_VOTE_TIME_TOO_SMALL = "VOTING_VOTE_TIME_TOO_SMALL"; + string private constant ERROR_OBJ_TIME_TOO_BIG = "VOTING_OBJ_TIME_TOO_BIG"; string private constant ERROR_INIT_OBJ_TIME_TOO_BIG = "VOTING_INIT_OBJ_TIME_TOO_BIG"; string private constant ERROR_CAN_NOT_VOTE_FOR = "VOTING_CAN_NOT_VOTE_FOR"; string private constant ERROR_ZERO_ADDRESS_PASSED = "VOTING_ZERO_ADDRESS_PASSED"; @@ -167,7 +167,7 @@ contract Voting is IForwarder, AragonApp { external auth(UNSAFELY_MODIFY_VOTE_TIME_ROLE) { - require(_voteTime > objectionPhaseTime, ERROR_CHANGE_VOTE_TIME); + require(_voteTime > objectionPhaseTime, ERROR_VOTE_TIME_TOO_SMALL); voteTime = _voteTime; emit ChangeVoteTime(_voteTime); @@ -181,7 +181,7 @@ contract Voting is IForwarder, AragonApp { external auth(UNSAFELY_MODIFY_VOTE_TIME_ROLE) { - require(voteTime > _objectionPhaseTime, ERROR_CHANGE_OBJECTION_TIME); + require(voteTime > _objectionPhaseTime, ERROR_OBJ_TIME_TOO_BIG); objectionPhaseTime = _objectionPhaseTime; emit ChangeObjectionPhaseTime(_objectionPhaseTime); @@ -282,8 +282,9 @@ contract Voting is IForwarder, AragonApp { require(_isValidPhaseToVote(vote_, _supports), ERROR_CAN_NOT_VOTE); bool hasManagedToVote = false; + address voter; for (uint256 i = 0; i < _voters.length; ++i) { - address voter = _voters[i]; + voter = _voters[i]; require(_hasVotingPower(vote_, voter), ERROR_NO_VOTING_POWER); if (_canVoteFor(vote_, msg.sender, voter)) { _vote(_voteId, _supports, voter, true); @@ -495,6 +496,8 @@ contract Voting is IForwarder, AragonApp { /** * @dev Internal function to create a new vote + * @param _executionScript The execution script for the vote + * @param _metadata The metadata for the vote * @return voteId id for newly created vote */ function _newVote(bytes _executionScript, string _metadata) internal returns (uint256 voteId) { @@ -516,9 +519,13 @@ contract Voting is IForwarder, AragonApp { } /** - * @dev Internal function to cast a vote or object to. - @dev It assumes that voter can support or object to the vote - */ + * @dev Internal function to cast a vote or object to. + * @dev It assumes that voter can support or object to the vote + * @param _voteId The identifier of the vote + * @param _supports Whether the voter supports the vote or not + * @param _voter The address of the voter + * @param _isDelegate Whether the voter is a delegate or not + */ function _vote(uint256 _voteId, bool _supports, address _voter, bool _isDelegate) internal { Vote storage vote_ = votes[_voteId]; @@ -554,6 +561,7 @@ contract Voting is IForwarder, AragonApp { /** * @dev Internal function to execute a vote. It assumes the queried vote exists. + * @param _voteId The identifier of the vote */ function _executeVote(uint256 _voteId) internal { require(_canExecute(_voteId), ERROR_CAN_NOT_EXECUTE); @@ -562,6 +570,7 @@ contract Voting is IForwarder, AragonApp { /** * @dev Unsafe version of _executeVote that assumes you have already checked if the vote can be executed and exists + * @param _voteId The identifier of the vote */ function _unsafeExecuteVote(uint256 _voteId) internal { Vote storage vote_ = votes[_voteId]; @@ -613,6 +622,7 @@ contract Voting is IForwarder, AragonApp { * @param _delegate the address of the delegate * @param _offset the number of delegated voters from the start of the list to skip * @param _limit the number of delegated voters to return + * @param _blockNumber the block number to get the voting power * @return the array of delegated voters * @return the array of voting power of delegated voters */ @@ -628,8 +638,9 @@ contract Voting is IForwarder, AragonApp { uint256 returnCount = _offset.add(_limit) > delegatedVotersCount ? delegatedVotersCount.sub(_offset) : _limit; votersList = new address[](returnCount); votingPowerList = new uint256[](returnCount); + address voter; for (uint256 i = 0; i < returnCount; ++i) { - address voter = delegatedVoters[_delegate].addresses[_offset + i]; + voter = delegatedVoters[_delegate].addresses[_offset + i]; votersList[i] = voter; votingPowerList[i] = token.balanceOfAt(voter, _blockNumber); } @@ -638,6 +649,7 @@ contract Voting is IForwarder, AragonApp { /** * @dev Internal function to check if a vote can be executed. It assumes the queried vote exists. + * @param _voteId The identifier of the vote * @return True if the given vote can be executed, false otherwise */ function _canExecute(uint256 _voteId) internal view returns (bool) { @@ -669,6 +681,8 @@ contract Voting is IForwarder, AragonApp { /** * @dev Internal function to check if the vote is open and given option is applicable at the current phase. * It assumes the queried vote exists. + * @param _vote The queried vote + * @param _supports Whether the voter supports the vote or not * @return True if the given voter can participate a certain vote, false otherwise */ function _isValidPhaseToVote(Vote storage _vote, bool _supports) internal view returns (bool) { @@ -697,11 +711,12 @@ contract Voting is IForwarder, AragonApp { /** * @dev Internal function to get the current phase of the vote. It assumes the queried vote exists. + * @param _vote The queried vote * @return VotePhase.Main if one can vote 'yes' or 'no', VotePhase.Objection if one can vote only 'no' or VotePhase.Closed if no votes are accepted */ - function _getVotePhase(Vote storage vote_) internal view returns (VotePhase) { + function _getVotePhase(Vote storage _vote) internal view returns (VotePhase) { uint64 timestamp = getTimestamp64(); - uint64 voteTimeEnd = vote_.startDate.add(voteTime); + uint64 voteTimeEnd = _vote.startDate.add(voteTime); if (timestamp < voteTimeEnd.sub(objectionPhaseTime)) { return VotePhase.Main; } @@ -713,14 +728,19 @@ contract Voting is IForwarder, AragonApp { /** * @dev Internal function to check if a vote is still open for both support and objection + * @param _vote The queried vote * @return True if less than voteTime has passed since the vote start */ - function _isVoteOpen(Vote storage vote_) internal view returns (bool) { - return getTimestamp64() < vote_.startDate.add(voteTime) && !vote_.executed; + function _isVoteOpen(Vote storage _vote) internal view returns (bool) { + return getTimestamp64() < _vote.startDate.add(voteTime) && !_vote.executed; } /** * @dev Calculates whether `_value` is more than a percentage `_pct` of `_total` + * @param _value The value to compare + * @param _total The total value + * @param _pct The percentage to compare + * @return True if `_value` is more than a percentage `_pct` of `_total`, false otherwise */ function _isValuePct(uint256 _value, uint256 _total, uint256 _pct) internal pure returns (bool) { if (_total == 0) { From 22fe3189c3a38a7d87a06e7c681fb90351b4897f Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Fri, 22 Mar 2024 14:01:05 +0100 Subject: [PATCH 041/100] fix: remove inconsistent balance check --- apps/voting/contracts/Voting.sol | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index f1257818f..bd596e4f2 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -251,9 +251,6 @@ contract Voting is IForwarder, AragonApp { address prevDelegate = delegates[msg.sender].delegate; require(_delegate != prevDelegate, ERROR_DELEGATE_SAME_AS_PREV); - uint256 votingPower = token.balanceOfAt(msg.sender, getBlockNumber64() - 1); - require(votingPower > 0, ERROR_NO_VOTING_POWER); - if (prevDelegate != address(0)) { _removeDelegatedAddressFor(prevDelegate, msg.sender); } From e7faa432a66715b43ea2be2c915b06ec156268a2 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Wed, 27 Mar 2024 23:30:38 +0100 Subject: [PATCH 042/100] feat: add holesky support for deployment --- apps/voting/.gitignore | 8 +++ apps/voting/accounts.sample.json | 15 ++++- apps/voting/hardhat.config.js | 9 ++- apps/voting/package.json | 2 +- yarn.lock | 99 +++++++++++++++++++++++++------- 5 files changed, 110 insertions(+), 23 deletions(-) create mode 100644 apps/voting/.gitignore diff --git a/apps/voting/.gitignore b/apps/voting/.gitignore new file mode 100644 index 000000000..ce8540925 --- /dev/null +++ b/apps/voting/.gitignore @@ -0,0 +1,8 @@ +# IDE Settings +.vscode + +# Local dev config +accounts.json + +# Local artifacts +tx-deploy-voting_for_upgrade.json diff --git a/apps/voting/accounts.sample.json b/apps/voting/accounts.sample.json index 6ee40033c..5c1a62acb 100644 --- a/apps/voting/accounts.sample.json +++ b/apps/voting/accounts.sample.json @@ -17,10 +17,23 @@ "projectId": "INFURA_PROJECT_ID" }, "etherscan": { - "apiKey": "ETHERSCAN_API_KEY" + "apiKey": "ETHERSCAN_API_KEY", + "customChains": [ + { + "network": "holesky", + "chainId": 17000, + "urls": { + "apiURL": "https://api-holesky.etherscan.io/api", + "browserURL": "https://holesky.etherscan.io" + } + } + ] }, "infura_ipfs": { "projectId": "", "projectSecret": "" + }, + "drpc": { + "key": "DRPC_KEY" } } diff --git a/apps/voting/hardhat.config.js b/apps/voting/hardhat.config.js index 0b03847cd..aa5624609 100644 --- a/apps/voting/hardhat.config.js +++ b/apps/voting/hardhat.config.js @@ -18,7 +18,8 @@ const ETH_ACCOUNT_NAME = process.env.ETH_ACCOUNT_NAME const accounts = readJson(`./accounts.json`) || { eth: { dev: 'remote' }, etherscan: { apiKey: undefined }, - infura: { projectId: undefined } + infura: { projectId: undefined }, + drpc: {key: undefined}, } const getNetConfig = (networkName, ethAccountName) => { @@ -64,6 +65,12 @@ const getNetConfig = (networkName, ethAccountName) => { chainId: 1, timeout: 60000 * 10 }, + holesky: { + ...base, + url: 'https://lb.drpc.org/ogrpc?network=holesky&dkey=' + accounts.drpc.key, + chainId: 17000, + timeout: 60000 * 10 + } } const netConfig = byNetName[networkName] return netConfig ? { [networkName]: netConfig } : {} diff --git a/apps/voting/package.json b/apps/voting/package.json index dfd01c2b0..78075dca5 100644 --- a/apps/voting/package.json +++ b/apps/voting/package.json @@ -41,7 +41,7 @@ "@nomiclabs/hardhat-truffle5": "^2.0.5", "@nomiclabs/hardhat-web3": "^2.0.0", "@nomiclabs/hardhat-ethers": "^2.0.2", - "@nomiclabs/hardhat-etherscan": "^2.1.6", + "@nomiclabs/hardhat-etherscan": "^3.1.8", "hardhat-abi-exporter": "^2.8.0", "hardhat-gas-reporter": "^1.0.8", "chai": "^4.2.0", diff --git a/yarn.lock b/yarn.lock index 7f9b50f4d..4344937ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -752,6 +752,11 @@ unique-filename "^1.1.1" which "^1.3.1" +"@fastify/busboy@^2.0.0": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d" + integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA== + "@lerna/add@3.21.0": version "3.21.0" resolved "https://registry.yarnpkg.com/@lerna/add/-/add-3.21.0.tgz#27007bde71cc7b0a2969ab3c2f0ae41578b4577b" @@ -1579,18 +1584,21 @@ resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.0.2.tgz#c472abcba0c5185aaa4ad4070146e95213c68511" integrity sha512-6quxWe8wwS4X5v3Au8q1jOvXYEPkS1Fh+cME5u6AwNdnI4uERvPlVjlgRWzpnb+Rrt1l/cEqiNRH9GlsBMSDQg== -"@nomiclabs/hardhat-etherscan@^2.1.6": - version "2.1.6" - resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-2.1.6.tgz#8d1502f42adc6f7b8ef16fb917c0b5a8780cb83a" - integrity sha512-gCvT5fj8GbXS9+ACS3BzrX0pzYHHZqAHCb+NcipOkl2cy48FakUXlzrCf4P4sTH+Y7W10OgT62ezD1sJ+/NikQ== +"@nomiclabs/hardhat-etherscan@^3.1.8": + version "3.1.8" + resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-3.1.8.tgz#3c12ee90b3733e0775e05111146ef9418d4f5a38" + integrity sha512-v5F6IzQhrsjHh6kQz4uNrym49brK9K5bYCq2zQZ729RYRaifI9hHbtmK+KkIVevfhut7huQFEQ77JLRMAzWYjQ== dependencies: "@ethersproject/abi" "^5.1.2" "@ethersproject/address" "^5.0.2" - cbor "^5.0.2" + cbor "^8.1.0" + chalk "^2.4.2" debug "^4.1.1" fs-extra "^7.0.1" - node-fetch "^2.6.0" + lodash "^4.17.11" semver "^6.3.0" + table "^6.8.0" + undici "^5.14.0" "@nomiclabs/hardhat-ganache@^2.0.1": version "2.0.1" @@ -2447,6 +2455,16 @@ ajv@^6.10.0, ajv@^6.12.3: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^8.0.1: + version "8.12.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + amdefine@>=0.0.4: version "1.0.1" resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" @@ -2517,7 +2535,7 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" -ansi-styles@^4.1.0: +ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== @@ -2708,6 +2726,11 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + async-each@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" @@ -3381,7 +3404,7 @@ big.js@^5.2.2: resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== -bignumber.js@*, bignumber.js@^9.0.0, bignumber.js@^9.0.1: +bignumber.js@*, bignumber.js@^9.0.0: version "9.0.1" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5" integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA== @@ -3973,13 +3996,12 @@ caw@^2.0.1: tunnel-agent "^0.6.0" url-to-options "^1.0.1" -cbor@^5.0.2: - version "5.2.0" - resolved "https://registry.yarnpkg.com/cbor/-/cbor-5.2.0.tgz#4cca67783ccd6de7b50ab4ed62636712f287a67c" - integrity sha512-5IMhi9e1QU76ppa5/ajP1BmMWZ2FHkhAhjeVKQ/EFCgYSEaeVaoGtL7cxJskf9oCCk+XjzaIdc3IuU/dbA/o2A== +cbor@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-8.1.0.tgz#cfc56437e770b73417a2ecbfc9caf6b771af60d5" + integrity sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg== dependencies: - bignumber.js "^9.0.1" - nofilter "^1.0.4" + nofilter "^3.1.0" chai@^4.2.0: version "4.3.4" @@ -8770,6 +8792,11 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" @@ -9335,6 +9362,11 @@ lodash.templatesettings@^4.0.0: dependencies: lodash._reinterpolate "^3.0.0" +lodash.truncate@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== + lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" @@ -10310,10 +10342,10 @@ node-releases@^2.0.0: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== -nofilter@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-1.0.4.tgz#78d6f4b6a613e7ced8b015cec534625f7667006e" - integrity sha512-N8lidFp+fCz+TD51+haYdbDGrcBWwuHX40F5+z0qkUjMJ5Tp+rdSuAkMJ9N9eoolDlEVTf6u5icM+cNKkKW2mA== +nofilter@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-3.1.0.tgz#c757ba68801d41ff930ba2ec55bab52ca184aa66" + integrity sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g== nopt@3.x: version "3.0.6" @@ -11907,7 +11939,7 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= -require-from-string@^2.0.0: +require-from-string@^2.0.0, require-from-string@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== @@ -12383,6 +12415,15 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + slide@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" @@ -12784,7 +12825,7 @@ string-width@^1.0.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" -"string-width@^1.0.2 || 2 || 3 || 4": +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -13028,6 +13069,17 @@ sync-rpc@^1.2.1: dependencies: get-port "^3.1.0" +table@^6.8.0: + version "6.8.2" + resolved "https://registry.yarnpkg.com/table/-/table-6.8.2.tgz#c5504ccf201213fa227248bdc8c5569716ac6c58" + integrity sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA== + dependencies: + ajv "^8.0.1" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + tape@^4.6.3: version "4.14.0" resolved "https://registry.yarnpkg.com/tape/-/tape-4.14.0.tgz#e4d46097e129817175b90925f2385f6b1bcfa826" @@ -13539,6 +13591,13 @@ underscore@^1.8.3: resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.1.tgz#0c1c6bd2df54b6b69f2314066d65b6cde6fcf9d1" integrity sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g== +undici@^5.14.0: + version "5.28.3" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.3.tgz#a731e0eff2c3fcfd41c1169a869062be222d1e5b" + integrity sha512-3ItfzbrhDlINjaP0duwnNsKpDQk3acHI3gVJ1z4fmwMK31k5G9OVIAMLSIaP6w4FaGkaAkN6zaQO9LUvZ1t7VA== + dependencies: + "@fastify/busboy" "^2.0.0" + union-value@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" From 1096e1623aacc5480dd98dd8cccf187fb933db45 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Fri, 29 Mar 2024 16:25:52 +0100 Subject: [PATCH 043/100] refactor: rename _vote param to vote_ --- apps/voting/contracts/Voting.sol | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index bd596e4f2..607aa96ba 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -678,12 +678,12 @@ contract Voting is IForwarder, AragonApp { /** * @dev Internal function to check if the vote is open and given option is applicable at the current phase. * It assumes the queried vote exists. - * @param _vote The queried vote + * @param vote_ The queried vote * @param _supports Whether the voter supports the vote or not * @return True if the given voter can participate a certain vote, false otherwise */ - function _isValidPhaseToVote(Vote storage _vote, bool _supports) internal view returns (bool) { - return _isVoteOpen(_vote) && (!_supports || _getVotePhase(_vote) == VotePhase.Main); + function _isValidPhaseToVote(Vote storage vote_, bool _supports) internal view returns (bool) { + return _isVoteOpen(vote_) && (!_supports || _getVotePhase(vote_) == VotePhase.Main); } function _isDelegateFor(address _delegate, address _voter) internal view returns (bool) { @@ -693,27 +693,27 @@ contract Voting is IForwarder, AragonApp { return delegates[_voter].delegate == _delegate; } - function _canVoteFor(Vote storage _vote, address _delegate, address _voter) internal view returns (bool) { - return _isDelegateFor(_delegate, _voter) && !_hasVotedDirectly(_vote, _voter); + function _canVoteFor(Vote storage vote_, address _delegate, address _voter) internal view returns (bool) { + return _isDelegateFor(_delegate, _voter) && !_hasVotedDirectly(vote_, _voter); } - function _hasVotedDirectly(Vote storage _vote, address _voter) internal view returns (bool) { - VoterState state = _vote.voters[_voter]; + function _hasVotedDirectly(Vote storage vote_, address _voter) internal view returns (bool) { + VoterState state = vote_.voters[_voter]; return state == VoterState.Yea || state == VoterState.Nay; } - function _hasVotingPower(Vote storage _vote, address _voter) internal view returns (bool) { - return token.balanceOfAt(_voter, _vote.snapshotBlock) > 0; + function _hasVotingPower(Vote storage vote_, address _voter) internal view returns (bool) { + return token.balanceOfAt(_voter, vote_.snapshotBlock) > 0; } /** * @dev Internal function to get the current phase of the vote. It assumes the queried vote exists. - * @param _vote The queried vote + * @param vote_ The queried vote * @return VotePhase.Main if one can vote 'yes' or 'no', VotePhase.Objection if one can vote only 'no' or VotePhase.Closed if no votes are accepted */ - function _getVotePhase(Vote storage _vote) internal view returns (VotePhase) { + function _getVotePhase(Vote storage vote_) internal view returns (VotePhase) { uint64 timestamp = getTimestamp64(); - uint64 voteTimeEnd = _vote.startDate.add(voteTime); + uint64 voteTimeEnd = vote_.startDate.add(voteTime); if (timestamp < voteTimeEnd.sub(objectionPhaseTime)) { return VotePhase.Main; } @@ -725,11 +725,11 @@ contract Voting is IForwarder, AragonApp { /** * @dev Internal function to check if a vote is still open for both support and objection - * @param _vote The queried vote + * @param vote_ The queried vote * @return True if less than voteTime has passed since the vote start */ - function _isVoteOpen(Vote storage _vote) internal view returns (bool) { - return getTimestamp64() < _vote.startDate.add(voteTime) && !_vote.executed; + function _isVoteOpen(Vote storage vote_) internal view returns (bool) { + return getTimestamp64() < vote_.startDate.add(voteTime) && !vote_.executed; } /** From f2f1eedb31ae7b2279b214d7a5f78e7c01e5b9d9 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Sun, 31 Mar 2024 23:13:46 +0200 Subject: [PATCH 044/100] refactor: comment out deprecated arguments names --- apps/voting/contracts/Voting.sol | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 607aa96ba..e57f15635 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -202,11 +202,11 @@ contract Voting is IForwarder, AragonApp { * @dev _executesIfDecided was deprecated to introduce a proper lock period between decision and execution. * @param _executionScript EVM script to be executed on approval * @param _metadata Vote metadata - * @param _castVote_deprecated Whether to also cast newly created vote - * @param _executesIfDecided_deprecated Whether to also immediately execute newly created vote if decided + * @dev _castVote_deprecated Whether to also cast newly created vote - DEPRECATED + * @dev _executesIfDecided_deprecated Whether to also immediately execute newly created vote if decided - DEPRECATED * @return voteId id for newly created vote */ - function newVote(bytes _executionScript, string _metadata, bool _castVote_deprecated, bool _executesIfDecided_deprecated) + function newVote(bytes _executionScript, string _metadata, bool /* _castVote_deprecated */, bool /* _executesIfDecided_deprecated */) external auth(CREATE_VOTES_ROLE) returns (uint256 voteId) @@ -221,9 +221,9 @@ contract Voting is IForwarder, AragonApp { * @dev _executesIfDecided was deprecated to introduce a proper lock period between decision and execution. * @param _voteId Id for vote * @param _supports Whether voter supports the vote - * @param _executesIfDecided_deprecated Whether the vote should execute its action if it becomes decided + * @dev _executesIfDecided_deprecated Whether the vote should execute its action if it becomes decided - DEPRECATED */ - function vote(uint256 _voteId, bool _supports, bool _executesIfDecided_deprecated) external voteExists(_voteId) { + function vote(uint256 _voteId, bool _supports, bool /* _executesIfDecided_deprecated */) external voteExists(_voteId) { Vote storage vote_ = votes[_voteId]; require(_isValidPhaseToVote(vote_, _supports), ERROR_CAN_NOT_VOTE); require(_hasVotingPower(vote_, msg.sender), ERROR_NO_VOTING_POWER); From e3cef8c8093a5cc93624739a98c3e5f77b14f122 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Mon, 1 Apr 2024 18:18:22 +0200 Subject: [PATCH 045/100] docs: add missing natspec comments --- apps/voting/contracts/Voting.sol | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index e57f15635..817558876 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -686,6 +686,12 @@ contract Voting is IForwarder, AragonApp { return _isVoteOpen(vote_) && (!_supports || _getVotePhase(vote_) == VotePhase.Main); } + /** + * @dev Internal function to check if the _delegate is a current delegate for the _voter + * @param _delegate address of the delegate + * @param _voter address of the voter + * @return True if _delegate is a current delegate for the _voter, false otherwise + */ function _isDelegateFor(address _delegate, address _voter) internal view returns (bool) { if (_delegate == address(0) || _voter == address(0)) { return false; @@ -693,15 +699,34 @@ contract Voting is IForwarder, AragonApp { return delegates[_voter].delegate == _delegate; } + /** + * @dev Internal function to check if the _delegate can vote on behalf of the _voter in the given vote + * @param vote_ The queried vote + * @param _delegate address of the delegate + * @param _voter address of the voter + * @return True if the _delegate can vote on behalf of the _voter in the given vote, false otherwise + */ function _canVoteFor(Vote storage vote_, address _delegate, address _voter) internal view returns (bool) { return _isDelegateFor(_delegate, _voter) && !_hasVotedDirectly(vote_, _voter); } + /** + * @dev Internal function to check if the _voter has voted by themselves in the given vote + * @param vote_ The queried vote + * @param _voter address of the voter + * @return True if the _voter has voted by themselves in the given vote, false otherwise + */ function _hasVotedDirectly(Vote storage vote_, address _voter) internal view returns (bool) { VoterState state = vote_.voters[_voter]; return state == VoterState.Yea || state == VoterState.Nay; } + /** + * @dev Internal function to get the voter's token balance at the vote's snapshot block + * @param vote_ The queried vote + * @param _voter address of the voter + * @return The voter's token balance at the vote's snapshot block + */ function _hasVotingPower(Vote storage vote_, address _voter) internal view returns (bool) { return token.balanceOfAt(_voter, vote_.snapshotBlock) > 0; } From b3030317965abedf142f4bf31e4ee7acff08ee5e Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 2 Apr 2024 10:10:48 +0200 Subject: [PATCH 046/100] test: simple delegation tests (#41) * test: add delegation state management unit tests * test: add delegation scenario tests (#43) * test: add delegation scenario tests * test: remove block with exception because it is pass in coverage * test: add delegation voting unit tests * test: remove outdated delegation tests --------- Co-authored-by: BATMAH69 --- apps/voting/hardhat.config.js | 2 +- apps/voting/test/voting.js | 1220 +++++++++++++++++++++++++++++---- 2 files changed, 1103 insertions(+), 119 deletions(-) diff --git a/apps/voting/hardhat.config.js b/apps/voting/hardhat.config.js index aa5624609..27578e3b7 100644 --- a/apps/voting/hardhat.config.js +++ b/apps/voting/hardhat.config.js @@ -42,7 +42,7 @@ const getNetConfig = (networkName, ethAccountName) => { blockGasLimit: 20000000, accounts: { mnemonic: 'hardhat', - count: 20, + count: 500, accountsBalance: '100000000000000000000000', gasPrice: 0 } diff --git a/apps/voting/test/voting.js b/apps/voting/test/voting.js index 0182dc0c9..ac0be251c 100644 --- a/apps/voting/test/voting.js +++ b/apps/voting/test/voting.js @@ -17,7 +17,7 @@ const VOTER_STATE = ['ABSENT', 'YEA', 'NAY', 'DELEGATE_YEA', 'DELEGATE_NAY'].red return state; }, {}) -contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, delegate1, delegate2, nonHolder]) => { +contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, delegate1, delegate2, nonHolder, ...spamHolders]) => { let votingBase, voting, token, executionTarget, aclP let CREATE_VOTES_ROLE, MODIFY_SUPPORT_ROLE, MODIFY_QUORUM_ROLE, UNSAFELY_MODIFY_VOTE_TIME_ROLE @@ -103,7 +103,7 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d }) for (const decimals of [0, 2, 18, 26]) { - context(`normal token supply, ${decimals} decimals`, () => { + context(`normal token supply, ${decimals} decimals`, () => { const neededSupport = pct16(50) const minimumAcceptanceQuorum = pct16(20) @@ -183,7 +183,7 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d const action = { to: executionTarget.address, calldata: executionTarget.contract.methods.execute().encodeABI() } script = encodeCallScript([action, action]) - const receipt = await voting.methods['newVote(bytes,string,bool,bool)'](script, 'metadata', false, false, { from: holder51 }); + const receipt = await voting.methods['newVote(bytes,string,bool,bool)'](script, 'metadata', false, false, { from: holder51 }) voteId = getEventArgument(receipt, 'StartVote', 'voteId') creator = getEventArgument(receipt, 'StartVote', 'creator') metadata = getEventArgument(receipt, 'StartVote', 'metadata') @@ -409,116 +409,6 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d }) }) }) - - context('voting for', () => { - let script, voteId, creator, metadata - - const neededSupport = pct16(50) - const minimumAcceptanceQuorum = pct16(20) - - beforeEach(async () => { - token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', decimals, 'n', true) // empty parameters minime - - await token.generateTokens(holder20, bigExp(20, decimals)) - await token.generateTokens(holder29, bigExp(29, decimals)) - await token.generateTokens(holder51, bigExp(51, decimals)) - await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, 0) - await voting.setDelegate(delegate1, {from: holder29}) - await voting.setDelegate(delegate1, {from: holder51}) - - executionTarget = await ExecutionTarget.new() - - const action = {to: executionTarget.address, calldata: executionTarget.contract.methods.execute().encodeABI()} - script = encodeCallScript([action, action]) - - const receipt = await voting.methods['newVote(bytes,string,bool,bool)'](script, 'metadata', false, false, {from: holder51}); - voteId = getEventArgument(receipt, 'StartVote', 'voteId') - creator = getEventArgument(receipt, 'StartVote', 'creator') - metadata = getEventArgument(receipt, 'StartVote', 'metadata') - }) - - - it('delegate can vote for voter', async () => { - const tx = await voting.attemptVoteFor(voteId, false, holder29, {from: delegate1}) - assertEvent(tx, 'CastVote', {expectedArgs: {voteId: voteId, voter: holder29, supports: false}}) - assertEvent(tx, 'CastVoteAsDelegate', {expectedArgs: {voteId: voteId, delegate: delegate1, voter: holder29, supports: false}}) - assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) - assertAmountOfEvents(tx, 'CastObjection', {expectedAmount: 0}) - assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 1}) - - const state = await voting.getVote(voteId) - const voterState = await voting.getVoterState(voteId, holder29) - - assertBn(state[7], bigExp(29, decimals), 'nay vote should have been counted') - assert.equal(voterState, VOTER_STATE.DELEGATE_NAY, 'holder29 should have delegate nay voter status') - }) - - it('delegate can vote for both voters', async () => { - const tx = await voting.attemptVoteForMultiple(voteId, false, [holder29, holder51], {from: delegate1}) - assertEvent(tx, 'CastVote', {expectedArgs: {voteId: voteId, voter: holder29, supports: false}}) - assertEvent(tx, 'CastVote', {index: 1, expectedArgs: {voteId: voteId, voter: holder51, supports: false}}) - assertEvent(tx, 'CastVoteAsDelegate', {expectedArgs: {voteId: voteId, delegate: delegate1, voter: holder29, supports: false}}) - assertEvent(tx, 'CastVoteAsDelegate', {index: 1, expectedArgs: {voteId: voteId, delegate: delegate1, voter: holder51, supports: false}}) - assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 2}) - assertAmountOfEvents(tx, 'CastObjection', {expectedAmount: 0}) - assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 2}) - - const state = await voting.getVote(voteId) - assertBn(state[7], bigExp(80, decimals), 'nay vote should have been counted') - - const voterState29 = await voting.getVoterState(voteId, holder29) - assert.equal(voterState29, VOTER_STATE.DELEGATE_NAY, 'holder29 should have delegate nay voter status') - - const voterState51 = await voting.getVoterState(voteId, holder51) - assert.equal(voterState51, VOTER_STATE.DELEGATE_NAY, 'holder51 should have delegate nay voter status') - }) - - it(`revert if both voters has voted before`, async () => { - await voting.vote(voteId, false, true, { from: holder29 }) - await voting.vote(voteId, false, true, { from: holder51 }) - await assertRevert( - voting.attemptVoteForMultiple(voteId, false, [holder29, holder51], {from: delegate1}), - ERRORS.VOTING_CAN_NOT_VOTE_FOR - ) - }) - - it(`delegate can vote for one of the two if the other one has voted before`, async () => { - await voting.vote(voteId, false, true, { from: holder51 }) - const tx = await voting.attemptVoteForMultiple(voteId, false, [holder29, holder51], {from: delegate1}) - assertEvent(tx, 'CastVoteAsDelegate', {expectedArgs: {voteId: voteId, delegate: delegate1, voter: holder29, supports: false}}) - assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 1}) - }) - - it(`voter can overwrite delegate's vote`, async () => { - await voting.attemptVoteFor(voteId, false, holder29, {from: delegate1}) - - const tx = await voting.vote(voteId, true, true, {from: holder29}) - assertEvent(tx, 'CastVote', {expectedArgs: {voteId: voteId, voter: holder29, supports: true}}) - assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) - assertAmountOfEvents(tx, 'CastObjection', {expectedAmount: 0}) - - const state = await voting.getVote(voteId) - assertBn(state[6], bigExp(29, decimals), 'yea vote should have been counted') - assertBn(state[7], bigExp(0, decimals), 'nay vote should have been reset') - - const voterState29 = await voting.getVoterState(voteId, holder29) - assert.equal(voterState29, VOTER_STATE.YEA, 'holder29 should have yea voter status') - }) - - it(`delegate can't overwrite voter's vote`, async () => { - await voting.attemptVoteFor(voteId, false, holder29, {from: delegate1}) - await voting.vote(voteId, true, true, {from: holder29}) - - await assertRevert( - voting.attemptVoteFor( - voteId, - false, - holder29, - { from: delegate1 } - ), ERRORS.VOTING_CAN_NOT_VOTE_FOR - ) - }) - }) } context('wrong initializations', () => { @@ -910,13 +800,84 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d const minimumAcceptanceQuorum = pct16(20) const decimals = 18 + const MAIN_PHASE = 0 + const OBJECTION_PHASE = 1 + + const LDO1 = bigExp(1, decimals) + const LDO20 = bigExp(20, decimals) + const LDO29 = bigExp(29, decimals) + const LDO51 = bigExp(51, decimals) + const LDO3 = bigExp(3, decimals) + const initBalance = { + [holder1]: LDO1, + [holder20]: LDO20, + [holder29]: LDO29, + [holder51]: LDO51, + } + + const setDelegate = async (delegate, holder ) => { + const tx = await voting.setDelegate(delegate, {from: holder}) + assertEvent(tx, 'SetDelegate', { + expectedArgs: {voter: holder, delegate} + }) + assertAmountOfEvents(tx, 'SetDelegate', {expectedAmount: 1}) + } + const attemptVoteFor = async (voteId, supports, holder, delegate) => { + const tx = await voting.attemptVoteFor(voteId, supports, holder, {from: delegate}) + assertEvent(tx, 'CastVote', { + expectedArgs: {voteId: voteId, voter: holder, supports: supports, stake: initBalance[holder]} + }) + assertEvent(tx, 'CastVoteAsDelegate', { + expectedArgs: {voteId: voteId, delegate: delegate, voter: holder, supports: supports, stake: initBalance[holder]} + }) + assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 1}) + } + const attemptVoteForMultiple = async (voteId, supports, holders, delegate) => { + const tx = await voting.attemptVoteForMultiple(voteId, supports, holders, {from: delegate}) + + for (const index of Object.keys(holders)){ + const holder = holders[index] + let stake + if (initBalance[holder]) { + stake = initBalance[holder] + } + if (!stake && spamHolders.includes(holder)) { + stake = LDO3 + } + assertEvent(tx, 'CastVote', { + index, + expectedArgs: {voteId, voter: holder, supports, stake} + }) + assertEvent(tx, 'CastVoteAsDelegate', { + index, + expectedArgs: {voteId, delegate, voter: holder, supports, stake} + }) + } + assertAmountOfEvents(tx, 'CastVote', {expectedAmount: holders.length}) + assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: holders.length}) + } + const vote = async (voteId, supports, exec, holder) => { + const tx = await voting.vote(voteId, supports, exec, {from: holder}) + assertEvent(tx, 'CastVote', { + expectedArgs: {voteId: voteId, voter: holder, supports, stake: initBalance[holder]} + }) + assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) + } + const verifyVoteYN = async (voteId, yes, no) => { + const { yea, nay } = await voting.getVote(voteId) + + assert.equal(yea.toString(), yes.toString()) + assert.equal(nay.toString(), no.toString()) + } + beforeEach(async () => { token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', decimals, 'n', true) // empty parameters minime + for (const [holder, balance] of Object.entries(initBalance)){ + await token.generateTokens(holder, balance) + } - await token.generateTokens(holder20, bigExp(20, decimals)) - await token.generateTokens(holder29, bigExp(29, decimals)) - await token.generateTokens(holder51, bigExp(51, decimals)) - await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, 0) + await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, objectionPhase) executionTarget = await ExecutionTarget.new() }) @@ -991,5 +952,1028 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d const delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 2))[0] assertArraysEqualAsSets(delegatedVoters, [holder29, holder51], 'delegate1 should be a delegate of holder29 and holder51') }) + + // Multiple voters with non-zero balances of governance token are delegating their voting + // power to a single delegate. The voting starts and the delegate is voting for all of them - first + // they get the full list of voters with their balance snapshot and vote states of the given voting, + // then they vote for that list. + it('delegate can manage several voters and vote for all (voteFor)', async () => { + const delegateList= [ [delegate1, holder1], [delegate1, holder20] ] + + for (const [delegate, holder] of delegateList) { + setDelegate(delegate, holder) + } + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [holder1,holder20]) + + for (const holder of delegatedVotersData[0]) { + await attemptVoteFor(voteId, false, holder, delegate1) + } + + await verifyVoteYN(voteId, 0, LDO1.add(LDO20)) + const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) + }) + + // Multiple voters with non-zero balances of governance token are delegating their voting + // power to a single delegate. The voting starts and the delegate is voting for all of them - first + // they get the full list of voters with their balance snapshot and vote states of the given voting, + // then they vote for that list. + it('delegate can manage several voters and vote all (voteForMulti)', async () => { + const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] + for (const [delegate, holder] of delegateList) { + await setDelegate(delegate, holder) + } + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) + + assertArraysEqualAsSets(delegatedVotersData[0], [holder29,holder51]) + + await attemptVoteForMultiple(voteId, true, delegatedVotersData[0], delegate2) + await verifyVoteYN(voteId, LDO51.add(LDO29), 0) + + const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) + }) + + // Multiple voters with non-zero balances of governance token are delegating their voting + // power to a single delegate. The voting starts and the delegate is voting for one of them. + it('delegate can manage several voters and vote for first (voteFor)', async () => { + const delegateList= [ [delegate1, holder1], [delegate1, holder20] ] + + for (const [delegate, holder] of delegateList) { + setDelegate(delegate, holder) + } + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [holder1, holder20]) + + const holder = delegatedVotersData[0][0] + await attemptVoteFor(voteId, false, holder, delegate1) + + await verifyVoteYN(voteId, 0, LDO1) + + const voterState = await voting.getVotersStateAtVote(voteId, [holder]) + + assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) + }) + + // Multiple voters with non-zero balances of governance token are delegating their voting + // power to a single delegate. The voting starts and the delegate is voting for one of them. + it('delegate can manage several voters and vote for first (voteForMulti)', async () => { + const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] + for (const [delegate, holder] of delegateList) { + setDelegate(delegate, holder) + } + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) + + assertArraysEqualAsSets(delegatedVotersData[0], [ holder29, holder51 ]) + + const holder = delegatedVotersData[0][0] + await attemptVoteForMultiple(voteId, true, [holder], delegate2) + + await verifyVoteYN(voteId, LDO29, 0) + + const voterState = await voting.getVotersStateAtVote(voteId, [holder]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) + }) + + // A delegated voter can overwrite a delegate's vote. + it('delegated voter can overwrite a delegates vote (voteFor)', async () => { + const [ delegate, holder] = [ delegate1, holder1 ] + + setDelegate(delegate, holder) + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [ holder1 ]) + + const supports = false + await attemptVoteFor(voteId, supports, holder, delegate1) + await vote( voteId, !supports, false, holder) + + await verifyVoteYN(voteId, LDO1, 0) + + const voterState = await voting.getVotersStateAtVote(voteId, [holder]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.YEA.toString()]) + }) + + // A delegated voter can overwrite a delegate's vote. + it('delegated voter can overwrite a delegates vote (voteForMulti)', async () => { + const [ delegate, holder] = [ delegate2, holder29 ] + + await setDelegate(delegate, holder) + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [holder29]) + + const supports = true + await attemptVoteForMultiple(voteId, supports, [holder], delegate2) + await vote(voteId, !supports, false, holder) + + await verifyVoteYN(voteId, 0 , LDO29) + + const voterState = await voting.getVotersStateAtVote(voteId, [holder]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.NAY.toString()]) + }) + + // A delegate can vote for a voter that delegated them their voting power during the active + // phase of the vote. + it('delegate can vote for a voter that delegated them their voting power during the active phase (voteFor)', async () => { + const [ delegate, holder] = [ delegate2, holder1 ] + + await setDelegate(delegate, holder) + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + const { phase } = await voting.getVote(voteId) + assert.equal(phase, MAIN_PHASE) + + await setDelegate(delegate1, holder) + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [ holder1 ]) + + await attemptVoteFor(voteId, false, holder, delegate1) + + await verifyVoteYN(voteId, 0, LDO1) + + const voterState = await voting.getVotersStateAtVote(voteId, [holder]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) + }) + + // A delegate can vote for a voter that delegated them their voting power during the active + // phase of the vote. + it('delegate can vote for a voter that delegated them their voting power during the active phase (voteForMulti)', async () => { + const [ delegate, holder] = [ delegate1, holder29 ] + + await setDelegate(delegate, holder) + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + const { phase } = await voting.getVote(voteId) + assert.equal(phase, MAIN_PHASE) + + await setDelegate(delegate2, holder) + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [holder29]) + + await attemptVoteForMultiple(voteId, true, [holder], delegate2) + + await verifyVoteYN(voteId, LDO29, 0) + + const voterState = await voting.getVotersStateAtVote(voteId, [holder]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) + }) + + // A delegate can't vote for a voter that acquired voting power during the active phase of the vote. + it('delegate cant vote for a voter that acquired voting power during the active phase of the vote (voteFor)', async () => { + const [ delegate, holder] = [ delegate1, holder1 ] + + await setDelegate(delegate, holder) + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + const { phase } = await voting.getVote(voteId) + assert.equal(phase, MAIN_PHASE) + + await setDelegate(delegate2, holder) + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [ ]) + + await assertRevert(voting.attemptVoteFor(voteId, false, holder, {from: delegate1}), ERRORS.VOTING_CAN_NOT_VOTE_FOR) + }) + + // A delegate can't vote for a voter that acquired voting power during the active phase of the vote. + it('delegate cant vote for a voter that acquired voting power during the active phase of the vote (voteForMulti)', async () => { + const [ delegate, holder] = [ delegate2, holder29 ] + + await setDelegate(delegate, holder) + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + const { phase } = await voting.getVote(voteId) + assert.equal(phase, MAIN_PHASE) + + await setDelegate(delegate1, holder) + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], []) + + await assertRevert(voting.attemptVoteForMultiple(voteId, true, [holder], {from: delegate2}), ERRORS.VOTING_CAN_NOT_VOTE_FOR) + + }) + + // If a delegated voter lost or gain some voting power after the start of the vote, a delegate + // would still apply the full voting power of the delegated voter (at the vote's snapshot) + it('delegate vote by snapshot vp not current (voteFor)', async () => { + const delegateList= [ [delegate1, holder1], [delegate1, holder20] ] + + for (const [delegate, holder] of delegateList) { + await setDelegate(delegate, holder) + } + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + await token.generateTokens(holder1, bigExp(2, decimals)) + await token.destroyTokens(holder20, bigExp(5, decimals)) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [holder1,holder20]) + + for (const holder of delegatedVotersData[0]) { + await attemptVoteFor(voteId, false, holder, delegate1) + } + await verifyVoteYN(voteId, 0, LDO1.add(LDO20)) + + const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) + }) + + // If a delegated voter lost or gain some voting power after the start of the vote, a delegate + // would still apply the full voting power of the delegated voter (at the vote's snapshot) + it('delegate vote by snapshot vp not current (voteForMulti)', async () => { + const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] + for (const [delegate, holder] of delegateList) { + await setDelegate(delegate, holder) + } + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + await token.generateTokens(holder29, bigExp(2, decimals)) + await token.destroyTokens(holder51, bigExp(5, decimals)) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [holder29,holder51]) + + await attemptVoteForMultiple(voteId, true, delegatedVotersData[0], delegate2) + await verifyVoteYN(voteId, LDO51.add(LDO29), 0) + + const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) + }) + + // A delegate can vote for all their delegated voters even if delegate voted for them before + // for different option (change mind) + it('delegate change mind (voteFor)', async () => { + const delegateList = [[delegate1, holder1], [delegate1, holder20]] + + for (const [delegate, holder] of delegateList) { + await setDelegate(delegate, holder) + } + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) + + assertArraysEqualAsSets(delegatedVotersData[0], [holder1, holder20]) + + for (const holder of delegatedVotersData[0]) { + await attemptVoteFor(voteId, false, holder, delegate1) + } + await verifyVoteYN(voteId,0, LDO1.add(LDO20)) + + for (const holder of delegatedVotersData[0]) { + await attemptVoteFor(voteId, true, holder, delegate1) + } + await verifyVoteYN(voteId, LDO1.add(LDO20), 0) + + const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) + }) + + // A delegate can vote for all their delegated voters even if delegate voted for them before + // for different option (change mind) + it('delegate change mind (voteForMulti)', async () => { + const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] + for (const [delegate, holder] of delegateList) { + await setDelegate(delegate, holder) + } + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [holder29,holder51]) + + + await attemptVoteForMultiple(voteId, true, delegatedVotersData[0], delegate2) + await verifyVoteYN(voteId, LDO51.add(LDO29), 0) + + await attemptVoteForMultiple(voteId, false, delegatedVotersData[0], delegate2) + await verifyVoteYN(voteId, 0, LDO51.add(LDO29)) + + const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) + }) + + // A delegate can vote for all their delegated voters even if delegate voted for them before + // for "no" option during the objection phase.(change mind) + it('delegate vote "yes" in main phase, delegate vote "no" in objection (voteFor)', async () => { + const delegateList = [[delegate1, holder1], [delegate1, holder20]] + + for (const [delegate, holder] of delegateList) { + await setDelegate(delegate, holder) + } + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [holder1, holder20]) + + for (const holder of delegatedVotersData[0]) { + await attemptVoteFor(voteId, true, holder, delegate1) + } + + await voting.mockIncreaseTime(votingDuration - objectionPhase) + await verifyVoteYN(voteId, LDO1.add(LDO20), 0) + + const { phase } = await voting.getVote(voteId) + assert.equal(phase, OBJECTION_PHASE) + + for (const holder of delegatedVotersData[0]) { + await attemptVoteFor(voteId, false, holder, delegate1) + } + await verifyVoteYN(voteId, 0, LDO1.add(LDO20)) + + const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) + }) + + // A delegate can vote for all their delegated voters even if delegate voted for them before + // for "no" option during the objection phase.(change mind) + it('delegate vote "yes" in main phase, delegate vote "no" in objection (voteForMulti)', async () => { + const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] + for (const [delegate, holder] of delegateList) { + await setDelegate(delegate, holder) + } + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [holder29,holder51]) + + await attemptVoteForMultiple(voteId, true, delegatedVotersData[0], delegate2) + + await voting.mockIncreaseTime(votingDuration - objectionPhase) + await verifyVoteYN(voteId, LDO51.add(LDO29), 0) + + const { phase } = await voting.getVote(voteId) + assert.equal(phase, OBJECTION_PHASE) + + await attemptVoteForMultiple(voteId, false, delegatedVotersData[0], delegate2) + + await verifyVoteYN(voteId, 0, LDO51.add(LDO29)) + + const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) + }) + + // A new delegate can vote "no" in objection phase for all their delegated voters + // even if old delegate voted for "yes" them before + it('delegate vote "yes" in main phase, new delegate vote "no" in objection (voteFor)', async () => { + const delegateList = [[delegate1, holder1], [delegate1, holder20]] + + for (const [delegate, holder] of delegateList) { + await setDelegate(delegate, holder) + } + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [holder1, holder20]) + + for (const holder of delegatedVotersData[0]) { + await attemptVoteFor(voteId, true, holder, delegate1) + } + + await voting.mockIncreaseTime(votingDuration - objectionPhase) + await verifyVoteYN(voteId, LDO1.add(LDO20), 0) + + const { phase } = await voting.getVote(voteId) + assert.equal(phase, OBJECTION_PHASE) + + const objectorList = [[delegate2, holder1], [delegate2, holder20]] + + for (const [delegate, holder] of objectorList) { + await setDelegate(delegate, holder) + } + + for (const [ delegate , holder] of objectorList) { + await attemptVoteFor(voteId, false, holder, delegate) + } + await verifyVoteYN(voteId, 0, LDO1.add(LDO20)) + + const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) + }) + + // A new delegate can vote "no" in objection phase for all their delegated voters + // even if old delegate voted for "yes" them before + it('delegate vote "yes" in main phase, new delegate vote "no" in objection (voteForMulti)', async () => { + const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] + for (const [delegate, holder] of delegateList) { + await setDelegate(delegate, holder) + } + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [holder29,holder51]) + + await attemptVoteForMultiple(voteId, true, delegatedVotersData[0], delegate2) + + await voting.mockIncreaseTime(votingDuration - objectionPhase) + await verifyVoteYN(voteId, LDO51.add(LDO29), 0) + + const { phase } = await voting.getVote(voteId) + assert.equal(phase, OBJECTION_PHASE) + + const objectorList = [[delegate1, holder29], [delegate1, holder51]] + + for (const [delegate, holder] of objectorList) { + await setDelegate(delegate, holder) + } + + await attemptVoteForMultiple(voteId, false, delegatedVotersData[0], delegate1) + + await verifyVoteYN(voteId, 0, LDO51.add(LDO29)) + + const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) + }) + + // A delegate is voting "yea" at the last moment of a vote's main phase. Delegated voters + // should be able to overpower the delegate during the objection phase. + it('delegate vote in main phase, voter overpower in objection (voteFor)', async () => { + const delegateList = [[delegate1, holder1], [delegate1, holder20]] + + for (const [delegate, holder] of delegateList) { + await setDelegate(delegate, holder) + } + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [holder1, holder20]) + + for (const holder of delegatedVotersData[0]) { + await attemptVoteFor(voteId, true, holder, delegate1) + } + + await voting.mockIncreaseTime(votingDuration - objectionPhase) + await verifyVoteYN(voteId, LDO1.add(LDO20), 0) + + const { phase } = await voting.getVote(voteId) + assert.equal(phase, OBJECTION_PHASE) + + for (const holder of delegatedVotersData[0]) { + await vote(voteId, false, false, holder) + } + await verifyVoteYN(voteId, 0, LDO1.add(LDO20)) + + const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.NAY.toString()]) + }) + + // A delegate is voting "yea" at the last moment of a vote's main phase. Delegated voters + // should be able to overpower the delegate during the objection phase. + it('delegate vote in main phase, voter overpower in objection (voteForMulti)', async () => { + const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] + for (const [delegate, holder] of delegateList) { + await setDelegate(delegate, holder) + } + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [holder29,holder51]) + + await attemptVoteForMultiple(voteId, true, delegatedVotersData[0], delegate2) + + await voting.mockIncreaseTime(votingDuration - objectionPhase) + await verifyVoteYN(voteId, LDO51.add(LDO29), 0) + + const { phase } = await voting.getVote(voteId) + assert.equal(phase, OBJECTION_PHASE) + + for (const holder of delegatedVotersData[0]) { + await vote(voteId, false, false, holder) + } + await verifyVoteYN(voteId, 0, LDO51.add(LDO29)) + + const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.NAY.toString()]) + }) + // If a delegate was spammed by a large amount of fake delegated voters, they can still easily + // retrieve an actual voters list and vote for that list. + it('delegate can vote after spam', async () => { + await setDelegate(delegate2, holder29) + for (const holder of spamHolders) { + await token.generateTokens(holder, LDO3) + await setDelegate(delegate2, holder) + } + await setDelegate(delegate2, holder51) + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 600, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [holder29, ...spamHolders, holder51]) + + await attemptVoteForMultiple(voteId, true, [holder29, holder51], delegate2) + + await verifyVoteYN(voteId, LDO51.add(LDO29), 0) + + const voterState = await voting.getVotersStateAtVote(voteId, [holder29, holder51]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) + + const voterStateSpam = await voting.getVotersStateAtVote(voteId, spamHolders) + assertArraysEqualAsSets(voterStateSpam, [VOTER_STATE.ABSENT.toString()]) + }).timeout(60_000); + + ///end + }) + + context('delegation state management', () => { + const neededSupport = pct16(50) + const minimumAcceptanceQuorum = pct16(20) + const decimals = 18 + const defaultLimit = 100 + + beforeEach(async () => { + token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', decimals, 'n', true) // empty parameters minime + + await token.generateTokens(holder20, bigExp(20, decimals)) + await token.generateTokens(holder29, bigExp(29, decimals)) + await token.generateTokens(holder51, bigExp(51, decimals)) + await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, 0) + + executionTarget = await ExecutionTarget.new() + }) + + it(`voter can assign themself a delegate`, async () => { + const tx = await voting.setDelegate(delegate1, {from: holder29}) + assertEvent(tx, 'SetDelegate', { + expectedArgs: {voter: holder29, delegate: delegate1} + }) + assertAmountOfEvents(tx, 'SetDelegate', {expectedAmount: 1}) + + const delegate = await voting.getDelegate(holder29) + assert.equal(delegate, delegate1, 'holder29 should have delegate1 as a delegate') + + const delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, defaultLimit))[0] + assertArraysEqualAsSets(delegatedVoters, [holder29], 'delegate1 should be a delegate of holder29') + }) + + it(`voter can change their assigned delegate`, async () => { + await voting.setDelegate(delegate1, {from: holder29}) + + const tx = await voting.setDelegate(delegate2, {from: holder29}) + assertEvent(tx, 'SetDelegate', { + expectedArgs: {voter: holder29, delegate: delegate2} + }) + assertAmountOfEvents(tx, 'SetDelegate', {expectedAmount: 1}) + + const delegate = await voting.getDelegate(holder29) + assert.equal(delegate, delegate2, 'holder29 should have delegate2 as a delegate') + + const delegatedVoters = (await voting.getDelegatedVoters(delegate2, 0, defaultLimit))[0] + assertArraysEqualAsSets(delegatedVoters, [holder29], 'delegate2 should be a delegate of holder29') + }) + + it(`multiple voters can assign themselves the delegate`, async () => { + await voting.setDelegate(delegate1, {from: holder29}) + await voting.setDelegate(delegate1, {from: holder20}) + + const delegate29 = await voting.getDelegate(holder29) + assert.equal(delegate29, delegate1, 'holder29 should have delegate1 as a delegate') + + const delegate20 = await voting.getDelegate(holder20) + assert.equal(delegate20, delegate1, 'holder20 should have delegate1 as a delegate') + + + const delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, defaultLimit))[0] + assertArraysEqualAsSets(delegatedVoters, [holder29, holder20], 'delegate1 should be a delegate of both holder29 and holder20') + }) + + it(`voter can't set the zero address as a delegate`, async () => { + await assertRevert( + voting.setDelegate(ZERO_ADDRESS, {from: holder29}), + ERRORS.VOTING_ZERO_ADDRESS_PASSED + ) + }) + + it(`voter can't assign themself as a delegate`, async () => { + await assertRevert( + voting.setDelegate(holder29, {from: holder29}), + ERRORS.VOTING_SELF_DELEGATE + ) + }) + + it(`voter can't assign their current delegate as a delegate`, async () => { + await voting.setDelegate(delegate1, {from: holder29}) + await assertRevert( + voting.setDelegate(delegate1, {from: holder29}), + ERRORS.VOTING_DELEGATE_SAME_AS_PREV + ) + }) + + it(`voter with zero token balance can't assign a delegate `, async () => { + await assertRevert( + voting.setDelegate(delegate1, {from: nonHolder}), + ERRORS.VOTING_NO_VOTING_POWER + ) + }) + + it(`voter can unassign their delegate`, async () => { + await voting.setDelegate(delegate1, {from: holder29}) + + const tx = await voting.resetDelegate({from: holder29}) + assertEvent(tx, 'ResetDelegate', { + expectedArgs: {voter: holder29, delegate: delegate1} + }) + assertAmountOfEvents(tx, 'ResetDelegate', {expectedAmount: 1}) + + const delegate = await voting.getDelegate(holder29) + assert.equal(delegate, ZERO_ADDRESS, `holder29 shouldn't have a delegate anymore`) + + const delegatedVoters = (await voting.getDelegatedVoters(delegate2, 0, defaultLimit))[0] + assertArraysEqualAsSets(delegatedVoters, [], 'delegatedVoters should be empty') + }) + + it(`voter can't unassign their delegate if they wasn't assigned before`, async () => { + await assertRevert( + voting.resetDelegate({from: holder29}), + ERRORS.VOTING_DELEGATE_NOT_SET + ) + }) + + it(`voter can unassign a delegate who has multiple delegated voters`, async () => { + await voting.setDelegate(delegate1, {from: holder20}) + await voting.setDelegate(delegate1, {from: holder29}) + + await voting.resetDelegate({from: holder29}) + const delegate = await voting.getDelegate(holder29) + assert.equal(delegate, ZERO_ADDRESS, `holder29 shouldn't have a delegate anymore`) + + const delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, defaultLimit))[0] + assertArraysEqualAsSets(delegatedVoters, [holder20], 'delegatedVoters should contain only holder20') + }) + + it(`multiple voters can unassign the delegate`, async () => { + await voting.setDelegate(delegate1, {from: holder20}) + await voting.setDelegate(delegate1, {from: holder29}) + await voting.setDelegate(delegate1, {from: holder51}) + + await voting.resetDelegate({from: holder29}) + await voting.resetDelegate({from: holder51}) + + const delegate20 = await voting.getDelegate(holder20) + assert.equal(delegate20, delegate1, `holder20 should still have delegate1 as a delegate`) + + const delegate29 = await voting.getDelegate(holder29) + assert.equal(delegate29, ZERO_ADDRESS, `holder29 shouldn't have a delegate anymore`) + + const delegate51 = await voting.getDelegate(holder51) + assert.equal(delegate51, ZERO_ADDRESS, `holder51 shouldn't have a delegate anymore`) + + const delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, defaultLimit))[0] + assertArraysEqualAsSets(delegatedVoters, [holder20], 'delegatedVoters should contain only holder20') + }) + + }) + + context('delegated voters getters', () => { + const neededSupport = pct16(50) + const minimumAcceptanceQuorum = pct16(20) + const decimals = 18 + const defaultLimit = 100 + const voters = [{ + account: holder1, + balance: bigExp(1, decimals) + },{ + account: holder2, + balance: bigExp(2, decimals) + }, { + account: holder20, + balance: bigExp(20, decimals) + }, { + account: holder29, + balance: bigExp(29, decimals) + }] + + beforeEach(async () => { + token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', decimals, 'n', true) // empty parameters minime + + await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, 0) + + for (let i = 0; i < voters.length; i++) { + await token.generateTokens(voters[i].account, voters[i].balance) + await voting.setDelegate(delegate1, {from: voters[i].account}) + } + + executionTarget = await ExecutionTarget.new() + }) + + it('should return correct delegated voters count', async () => { + const delegatedVotersCount = (await voting.getDelegatedVotersCount(delegate1)).toNumber() + assert(delegatedVotersCount === voters.length) + }) + + it(`revert if "_delegate" is zero address`, async () => { + await assertRevert( + voting.getDelegatedVotersCount(ZERO_ADDRESS), + ERRORS.VOTING_ZERO_ADDRESS_PASSED + ) + + await assertRevert( + voting.getDelegatedVoters(ZERO_ADDRESS, 0, defaultLimit), + ERRORS.VOTING_ZERO_ADDRESS_PASSED + ) + }) + + it(`revert if "_limit" is 0`, async () => { + await assertRevert( + voting.getDelegatedVoters(ZERO_ADDRESS, 0, defaultLimit), + ERRORS.VOTING_ZERO_ADDRESS_PASSED + ) + }) + + it(`if delegatedVoters array length is 0, return two empty arrays`, async () => { + const delegatdVotersData = await voting.getDelegatedVoters(nonHolder, 0, defaultLimit) + assert(delegatdVotersData[0].length === 0, 'votersList should be empty') + assert(delegatdVotersData[1].length === 0, 'votingPowerList should be empty') + }) + + it(`should return correct delegated voters data if offset + limit >= votersCount`, async () => { + const offset = 2 + const limit = 5 + const delegatedVotersData = await voting.getDelegatedVoters(delegate1, offset, limit) + const delegatedVotersCount = (await voting.getDelegatedVotersCount(delegate1)).toNumber() + const delegatedVotersCountToReturn = delegatedVotersCount - offset + + assert(delegatedVotersData[0].length === delegatedVotersCountToReturn) + assert(delegatedVotersData[1].length === delegatedVotersCountToReturn) + + const votersSlice = voters.slice(offset, delegatedVotersCount) + const votersListSlice = votersSlice.map(voter => voter.account) + assertArraysEqualAsSets(delegatedVotersData[0], votersListSlice, 'votersList should be correct') + + const votingPowerListSlice = votersSlice.map((voter) => voter.balance.toString()) + const votingPowerList = delegatedVotersData[1].map(votingPower => votingPower.toString()) + assertArraysEqualAsSets(votingPowerList, votingPowerListSlice, 'votingPowerList should be correct') + }) + + it(`should return correct delegated voters data if offset + limit < votersCount`, async () => { + const offset = 1 + const limit = 1 + const delegatedVotersData = await voting.getDelegatedVoters(delegate1, offset, limit) + + assert(delegatedVotersData[0].length === limit) + assert(delegatedVotersData[1].length === limit) + + const votersSlice = voters.slice(offset, offset + limit) + const votersListSlice = votersSlice.map(voter => voter.account) + assertArraysEqualAsSets(delegatedVotersData[0], votersListSlice, 'votersList should be correct') + + const votingPowerListSlice = votersSlice.map((voter) => voter.balance.toString()) + const votingPowerList = delegatedVotersData[1].map(votingPower => votingPower.toString()) + assertArraysEqualAsSets(votingPowerList, votingPowerListSlice, 'votingPowerList should be correct') + }) + + it(`revert if _voter is zero address`, async () => { + await assertRevert( + voting.getDelegate(ZERO_ADDRESS), + ERRORS.VOTING_ZERO_ADDRESS_PASSED + ) + }) + + it(`return zero address if no delegate`, async () => { + const delegate = await voting.getDelegate(nonHolder) + assert.equal(delegate, ZERO_ADDRESS, 'should return zero address') + }) + + it(`can get voter's delegate address`, async () => { + const delegate = await voting.getDelegate(holder1) + assert.equal(delegate, delegate1, 'should return delegate1 address') + }) }) -}) \ No newline at end of file + + context('voting as delegate', () => { + let script, voteId, creator, metadata + + const neededSupport = pct16(50) + const minimumAcceptanceQuorum = pct16(20) + const decimals = 18 + const voters = [{ + account: holder1, + balance: bigExp(1, decimals) + }, { + account: holder2, + balance: bigExp(2, decimals) + }, { + account: holder20, + balance: bigExp(20, decimals) + }, { + account: holder29, + balance: bigExp(29, decimals) + }, { + account: holder51, + balance: bigExp(51, decimals) + }] + + beforeEach(async () => { + token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', decimals, 'n', true) // empty parameters minime + + await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, objectionPhase) + + for (let i = 0; i < voters.length; i++) { + await token.generateTokens(voters[i].account, voters[i].balance) + await voting.setDelegate(delegate1, {from: voters[i].account}) + } + + executionTarget = await ExecutionTarget.new() + + const action = {to: executionTarget.address, calldata: executionTarget.contract.methods.execute().encodeABI()} + script = encodeCallScript([action, action]) + + await voting.resetDelegate({from: holder1}) + await token.transfer(holder51, bigExp(2, decimals), { from: holder2 }) + + const receipt = await voting.methods['newVote(bytes,string)'](script, 'metadata', {from: holder51}); + voteId = getEventArgument(receipt, 'StartVote', 'voteId') + creator = getEventArgument(receipt, 'StartVote', 'creator') + metadata = getEventArgument(receipt, 'StartVote', 'metadata') + }) + + it(`getVotersStateAtVote`, async () => { + await voting.vote(voteId, true, false, { from: holder20 }) + await voting.vote(voteId, false, false, { from: holder29 }) + await voting.attemptVoteForMultiple(voteId, false, [holder51], {from: delegate1}) + + const votersState = await voting.getVotersStateAtVote(voteId, [holder20, holder29, holder51]) + assert.equal(votersState[0], VOTER_STATE.YEA, `holder20 should have 'yea' state`) + assert.equal(votersState[1], VOTER_STATE.NAY, `holder29 should have 'nay' state`) + assert.equal(votersState[2], VOTER_STATE.DELEGATE_NAY, `holder51 should have 'delegateNay' state`) + }) + + it(`revert if vote does not exist`, async () => { + await assertRevert( + voting.attemptVoteForMultiple(voteId + 1, false, [holder51], {from: delegate1}), + ERRORS.VOTING_NO_VOTE + ) + }) + + it(`revert if vote has already ended`, async () => { + await voting.mockIncreaseTime(votingDuration + 1) + await assertRevert( + voting.attemptVoteForMultiple(voteId, false, [holder51], {from: delegate1}), + ERRORS.VOTING_CAN_NOT_VOTE + ) + }) + + it(`revert if vote has already been executed`, async () => { + await voting.vote(voteId, true, true, { from: holder51 }) + await voting.mockIncreaseTime(votingDuration + 1) + await voting.executeVote(voteId) + + await assertRevert( + voting.attemptVoteForMultiple(voteId, true, [holder51], {from: delegate1}), + ERRORS.VOTING_CAN_NOT_VOTE + ) + }) + + it(`revert if trying to vote 'yea' during objection phase`, async () => { + await voting.mockIncreaseTime(mainPhase + 200) + await assertRevert( + voting.attemptVoteForMultiple(voteId, true, [holder51], {from: delegate1}), + ERRORS.VOTING_CAN_NOT_VOTE + ) + }) + + it(`revert if one of the voters has 0 LDO`, async () => { + // holder 2 has 0 LDO + await assertRevert( + voting.attemptVoteForMultiple(voteId, true, [holder51, holder2, holder1], {from: delegate1}), + ERRORS.VOTING_NO_VOTING_POWER + ) + }) + + it(`one of the voters voted beforehand`, async () => { + await voting.vote(voteId, false, false, { from: holder29 }) + const tx = await voting.attemptVoteForMultiple(voteId, true, [holder20, holder29], {from: delegate1}); + + assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 1}) + assertEvent(tx, 'CastVote', {expectedArgs: {voteId, voter: holder20, supports: true}}) + assertEvent(tx, 'CastVoteAsDelegate', {expectedArgs: {voteId, delegate: delegate1, voter: holder20, supports: true}}) + + assert.equal(await voting.getVoterState(voteId, holder29), VOTER_STATE.NAY, `holder29 should have 'nay' state`) + assert.equal(await voting.getVoterState(voteId, holder20), VOTER_STATE.DELEGATE_YEA, `holder20 should have 'delegateYea' state`) + }) + + it(`vote for multiple with duplicates`, async () => { + const tx = await voting.attemptVoteForMultiple(voteId, false, [holder29, holder29], {from: delegate1}); + + assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 2}) + assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 2}) + assert.equal(await voting.getVoterState(voteId, holder29), VOTER_STATE.DELEGATE_NAY, `holder29 should have 'delegateNay' state`) + const vote = await voting.getVote(voteId) + assertBn(vote.nay, bigExp(29, decimals), 'nay should be 29') + }) + + it(`vote for empty list`, async () => { + await assertRevert( + voting.attemptVoteForMultiple(voteId, false, [], {from: delegate1}), + ERRORS.VOTING_CAN_NOT_VOTE_FOR + ) + }) + + it(`skipped vote for multiple for all voters from list`, async () => { + await voting.vote(voteId, false, false, { from: holder20 }) + await voting.vote(voteId, false, false, { from: holder29 }) + await assertRevert( + voting.attemptVoteForMultiple(voteId, false, [holder20, holder29], {from: delegate1}), + ERRORS.VOTING_CAN_NOT_VOTE_FOR + ) + }) + + it(`successful vote for multiple`, async () => { + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 100, voteId) + const delegatedVotersAddresses = delegatedVotersData[0] + const delegatedVotersVotingPower = delegatedVotersData[1] + const filteredDelegatedVoters = [] + for (let i = 0; i < delegatedVotersAddresses.length; i++) { + const votingPower = delegatedVotersVotingPower[i] + if (votingPower.gt(bigExp(0, decimals))) { + filteredDelegatedVoters.push({address: delegatedVotersAddresses[i], votingPower}) + } + } + const filteredDelegatedVotersAddresses = filteredDelegatedVoters.map(({address}) => address) + const tx = await voting.attemptVoteForMultiple( + voteId, + false, + filteredDelegatedVotersAddresses, + {from: delegate1} + ); + + // Check amount of events + assertAmountOfEvents(tx, 'CastVote', {expectedAmount: filteredDelegatedVoters.length}) + assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: filteredDelegatedVoters.length}) + + // Check events content + for (let i = 0; i < filteredDelegatedVoters.length; i++) { + const {address, votingPower} = filteredDelegatedVoters[i] + assertEvent(tx, 'CastVote', {index: i, expectedArgs: {voteId, voter: address, supports: false, stake: votingPower}}) + assertEvent(tx, 'CastVoteAsDelegate', {index: i, expectedArgs: {voteId, delegate: delegate1, voter: address, supports: false, stake: votingPower}}) + } + + // Check voters' state + const votersState = await voting.getVotersStateAtVote(voteId, filteredDelegatedVotersAddresses) + votersState.every((state) => { + assert.equal(state, VOTER_STATE.DELEGATE_NAY.toString(), `voter should have 'delegateNay' state`) + }) + + // Check applied VP + const vote = await voting.getVote(voteId) + const votingPowerSum = filteredDelegatedVoters.reduce( + (sum, {votingPower}) => sum.add(votingPower), + bigExp(0, decimals) + ) + + assertBn(vote.yea, bigExp(0, decimals), 'yea should be 0') + assertBn(vote.nay, votingPowerSum, 'nay should be sum of all VP') + }) + + it(`successful vote for single`, async () => { + const tx = await voting.attemptVoteFor(voteId, false, holder29, {from: delegate1}) + + // Check amount of events + assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 1}) + + const holder29VP = bigExp(29, decimals) + + // Check events content + assertEvent(tx, 'CastVote', { expectedArgs: {voteId, voter: holder29, supports: false, stake: holder29VP}}) + assertEvent(tx, 'CastVoteAsDelegate', { expectedArgs: {voteId, delegate: delegate1, voter: holder29, supports: false, stake: holder29VP}}) + + + // Check voter's state + assert.equal(await voting.getVoterState(voteId, holder29), VOTER_STATE.DELEGATE_NAY, `holder29 should have 'delegateNay' state`) + + // Check applied VP + const vote = await voting.getVote(voteId) + assertBn(vote.yea, bigExp(0, decimals), 'yea should be 0') + assertBn(vote.nay, holder29VP, 'nay should be holder29 VP') + }) + + }) + +}) From 42f8dc25111fda07ee22afdb9834269c37596f18 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 2 Apr 2024 10:29:38 +0200 Subject: [PATCH 047/100] test: remove outdated test --- apps/voting/test/voting.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/apps/voting/test/voting.js b/apps/voting/test/voting.js index ac0be251c..66def0e0f 100644 --- a/apps/voting/test/voting.js +++ b/apps/voting/test/voting.js @@ -1584,13 +1584,6 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d ) }) - it(`voter with zero token balance can't assign a delegate `, async () => { - await assertRevert( - voting.setDelegate(delegate1, {from: nonHolder}), - ERRORS.VOTING_NO_VOTING_POWER - ) - }) - it(`voter can unassign their delegate`, async () => { await voting.setDelegate(delegate1, {from: holder29}) From 54caa64864bea162ca6309fff4680bdcb28a0846 Mon Sep 17 00:00:00 2001 From: Roman Kolpakov Date: Tue, 16 Apr 2024 17:08:17 +0700 Subject: [PATCH 048/100] test: move out tests for delegation to the diiferent test file & remove duplicated tests --- apps/voting/test/delegation.js | 1147 +++++++++++++++++++++++++++++++ apps/voting/test/voting.js | 1179 +------------------------------- 2 files changed, 1149 insertions(+), 1177 deletions(-) create mode 100644 apps/voting/test/delegation.js diff --git a/apps/voting/test/delegation.js b/apps/voting/test/delegation.js new file mode 100644 index 000000000..6922433de --- /dev/null +++ b/apps/voting/test/delegation.js @@ -0,0 +1,1147 @@ +const ERRORS = require('./helpers/errors') +const assertArraysEqualAsSets = require('./helpers/assertArrayAsSets') +const { assertBn, assertRevert, assertAmountOfEvents, assertEvent } = require('@aragon/contract-helpers-test/src/asserts') +const { pct16, bn, bigExp, getEventArgument, ZERO_ADDRESS } = require('@aragon/contract-helpers-test') +const { newDao, installNewApp, encodeCallScript, ANY_ENTITY, EMPTY_CALLS_SCRIPT } = require('@aragon/contract-helpers-test/src/aragon-os') +const { assert } = require('chai') + +const Voting = artifacts.require('VotingMock') + +const MiniMeToken = artifacts.require('MiniMeToken') +const ExecutionTarget = artifacts.require('ExecutionTarget') + +const createdVoteId = receipt => getEventArgument(receipt, 'StartVote', 'voteId') + +const VOTER_STATE = ['ABSENT', 'YEA', 'NAY', 'DELEGATE_YEA', 'DELEGATE_NAY'].reduce((state, key, index) => { + state[key] = index; + return state; +}, {}) + +contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29, holder51, delegate1, delegate2, nonHolder, ...spamHolders]) => { + let votingBase, voting, token, executionTarget, aclP + let CREATE_VOTES_ROLE, MODIFY_SUPPORT_ROLE, MODIFY_QUORUM_ROLE, UNSAFELY_MODIFY_VOTE_TIME_ROLE + + const NOW = 1 + const mainPhase = 700 + const objectionPhase = 300 + const votingDuration = mainPhase + objectionPhase + + const APP_ID = '0x1234123412341234123412341234123412341234123412341234123412341234' + + before('load roles', async () => { + votingBase = await Voting.new() + CREATE_VOTES_ROLE = await votingBase.CREATE_VOTES_ROLE() + MODIFY_SUPPORT_ROLE = await votingBase.MODIFY_SUPPORT_ROLE() + MODIFY_QUORUM_ROLE = await votingBase.MODIFY_QUORUM_ROLE() + UNSAFELY_MODIFY_VOTE_TIME_ROLE = await votingBase.UNSAFELY_MODIFY_VOTE_TIME_ROLE() + }) + + beforeEach('deploy DAO with Voting app', async () => { + const { dao, acl } = await newDao(root) + voting = await Voting.at(await installNewApp(dao, APP_ID, votingBase.address, root)) + await voting.mockSetTimestamp(NOW) + await acl.createPermission(ANY_ENTITY, voting.address, CREATE_VOTES_ROLE, root, { from: root }) + await acl.createPermission(ANY_ENTITY, voting.address, MODIFY_SUPPORT_ROLE, root, { from: root }) + await acl.createPermission(ANY_ENTITY, voting.address, MODIFY_QUORUM_ROLE, root, { from: root }) + await acl.createPermission(ANY_ENTITY, voting.address, UNSAFELY_MODIFY_VOTE_TIME_ROLE, root, { from: root }) + aclP = acl + }) + + context('voting delegate', () => { + const neededSupport = pct16(50) + const minimumAcceptanceQuorum = pct16(20) + const decimals = 18 + + const MAIN_PHASE = 0 + const OBJECTION_PHASE = 1 + + const LDO1 = bigExp(1, decimals) + const LDO20 = bigExp(20, decimals) + const LDO29 = bigExp(29, decimals) + const LDO51 = bigExp(51, decimals) + const LDO3 = bigExp(3, decimals) + const initBalance = { + [holder1]: LDO1, + [holder20]: LDO20, + [holder29]: LDO29, + [holder51]: LDO51, + } + + const setDelegate = async (delegate, holder ) => { + const tx = await voting.setDelegate(delegate, {from: holder}) + assertEvent(tx, 'SetDelegate', { + expectedArgs: {voter: holder, delegate} + }) + assertAmountOfEvents(tx, 'SetDelegate', {expectedAmount: 1}) + } + const attemptVoteFor = async (voteId, supports, holder, delegate) => { + const tx = await voting.attemptVoteFor(voteId, supports, holder, {from: delegate}) + assertEvent(tx, 'CastVote', { + expectedArgs: {voteId: voteId, voter: holder, supports: supports, stake: initBalance[holder]} + }) + assertEvent(tx, 'CastVoteAsDelegate', { + expectedArgs: {voteId: voteId, delegate: delegate, voter: holder, supports: supports, stake: initBalance[holder]} + }) + assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 1}) + } + const attemptVoteForMultiple = async (voteId, supports, holders, delegate) => { + const tx = await voting.attemptVoteForMultiple(voteId, supports, holders, {from: delegate}) + + for (const index of Object.keys(holders)){ + const holder = holders[index] + let stake + if (initBalance[holder]) { + stake = initBalance[holder] + } + if (!stake && spamHolders.includes(holder)) { + stake = LDO3 + } + assertEvent(tx, 'CastVote', { + index, + expectedArgs: {voteId, voter: holder, supports, stake} + }) + assertEvent(tx, 'CastVoteAsDelegate', { + index, + expectedArgs: {voteId, delegate, voter: holder, supports, stake} + }) + } + assertAmountOfEvents(tx, 'CastVote', {expectedAmount: holders.length}) + assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: holders.length}) + } + const vote = async (voteId, supports, exec, holder) => { + const tx = await voting.vote(voteId, supports, exec, {from: holder}) + assertEvent(tx, 'CastVote', { + expectedArgs: {voteId: voteId, voter: holder, supports, stake: initBalance[holder]} + }) + assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) + } + const verifyVoteYN = async (voteId, yes, no) => { + const { yea, nay } = await voting.getVote(voteId) + + assert.equal(yea.toString(), yes.toString()) + assert.equal(nay.toString(), no.toString()) + } + + beforeEach(async () => { + token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', decimals, 'n', true) // empty parameters minime + for (const [holder, balance] of Object.entries(initBalance)){ + await token.generateTokens(holder, balance) + } + + await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, objectionPhase) + + executionTarget = await ExecutionTarget.new() + }) + + it(`voter can't set the zero address as a delegate`, async () => { + await assertRevert( + voting.setDelegate(ZERO_ADDRESS, {from: holder29}), + ERRORS.VOTING_ZERO_ADDRESS_PASSED + ) + }) + + it(`voter can't assign themself as a delegate`, async () => { + await assertRevert( + voting.setDelegate(holder29, {from: holder29}), + ERRORS.VOTING_SELF_DELEGATE + ) + }) + + it(`voter can't assign their current delegate as a delegate`, async () => { + await voting.setDelegate(delegate1, {from: holder29}) + await assertRevert( + voting.setDelegate(delegate1, {from: holder29}), + ERRORS.VOTING_DELEGATE_SAME_AS_PREV + ) + }) + + it(`voter can't unassign their delegate if they wasn't assigned before`, async () => { + await assertRevert( + voting.resetDelegate({from: holder29}), + ERRORS.VOTING_DELEGATE_NOT_SET + ) + }) + + it('voter can set delegate', async () => { + let delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] + assert.equal(delegatedVoters.length, 0, 'delegate1 should not be a delegate of anyone') + + const tx = await voting.setDelegate(delegate1, {from: holder29}) + assertEvent(tx, 'SetDelegate', { + expectedArgs: {voter: holder29, delegate: delegate1} + }) + assertAmountOfEvents(tx, 'SetDelegate', {expectedAmount: 1}) + + const delegate = await voting.getDelegate(holder29) + assert.equal(delegate, delegate1, 'holder29 should have delegate1 as a delegate') + + delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] + assertArraysEqualAsSets(delegatedVoters, [holder29], 'delegate1 should be a delegate of holder29') + }) + + it('voter can remove delegate', async () => { + let delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] + assert.equal(delegatedVoters.length, 0, 'delegate1 should not be a delegate of anyone') + + await voting.setDelegate(delegate1, {from: holder29}) + + delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] + assertArraysEqualAsSets(delegatedVoters, [holder29], 'delegate1 should be a delegate of holder29') + + const tx = await voting.resetDelegate({from: holder29}) + assertEvent(tx, 'ResetDelegate', { + expectedArgs: {voter: holder29, delegate: delegate1} + }) + assertAmountOfEvents(tx, 'ResetDelegate', {expectedAmount: 1}) + delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] + assertArraysEqualAsSets(delegatedVoters, [], 'delegate1 should not be a delegate of anyone') + }) + + it('voters can remove delegate', async () => { + let delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] + assert.equal(delegatedVoters.length, 0, 'delegate1 should not be a delegate of anyone') + + await voting.setDelegate(delegate1, {from: holder20}) + await voting.setDelegate(delegate1, {from: holder29}) + await voting.setDelegate(delegate1, {from: holder51}) + + const tx1 = await voting.resetDelegate({from: holder29}) + assertEvent(tx1, 'ResetDelegate', { + expectedArgs: {voter: holder29, delegate: delegate1} + }) + assertAmountOfEvents(tx1, 'ResetDelegate', {expectedAmount: 1}) + delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 5))[0] + assertArraysEqualAsSets(delegatedVoters, [holder20, holder51], 'delegate1 have holder20 and holder51 as a delegated voters') + + const tx2 = await voting.resetDelegate({from: holder51}) + assertEvent(tx2, 'ResetDelegate', { + expectedArgs: {voter: holder51, delegate: delegate1} + }) + assertAmountOfEvents(tx2, 'ResetDelegate', {expectedAmount: 1}) + delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] + assertArraysEqualAsSets(delegatedVoters, [holder20], 'delegate1 have only holder20 as a delegated voter') + }) + + it('voter can change delegate', async () => { + let delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] + assert.equal(delegatedVoters.length, 0, 'delegate1 should not be a delegate of anyone') + delegatedVoters = (await voting.getDelegatedVoters(delegate2, 0, 1))[0] + assert.equal(delegatedVoters.length, 0, 'delegate2 should not be a delegate of anyone') + + await voting.setDelegate(delegate1, {from: holder29}) + await voting.setDelegate(delegate2, {from: holder51}) + + await voting.setDelegate(delegate2, {from: holder29}) + + const delegatedVotersDelegate1 = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] + assertArraysEqualAsSets(delegatedVotersDelegate1, [], 'delegate1 should not be a delegate of anyone') + const delegatedVotersDelegate2 = (await voting.getDelegatedVoters(delegate2, 0, 2))[0] + assertArraysEqualAsSets(delegatedVotersDelegate2, [holder29, holder51], 'delegate2 should be a delegate of holder29 and holder51') + }) + + it('delegate can manage several voters', async () => { + let delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] + assert.equal(delegatedVoters.length, 0, 'delegate1 should not be a delegate of anyone') + + const tx1 = await voting.setDelegate(delegate1, {from: holder29}) + assertEvent(tx1, 'SetDelegate', { + expectedArgs: {voter: holder29, delegate: delegate1} + }) + assertAmountOfEvents(tx1, 'SetDelegate', {expectedAmount: 1}) + + const tx2 = await voting.setDelegate(delegate1, {from: holder51}) + assertEvent(tx2, 'SetDelegate', { + expectedArgs: {voter: holder51, delegate: delegate1} + }) + assertAmountOfEvents(tx2, 'SetDelegate', {expectedAmount: 1}) + + delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 2))[0] + assertArraysEqualAsSets(delegatedVoters, [holder29, holder51], 'delegate1 should be a delegate of holder29 and holder51') + }) + + // Multiple voters with non-zero balances of governance token are delegating their voting + // power to a single delegate. The voting starts and the delegate is voting for all of them - first + // they get the full list of voters with their balance snapshot and vote states of the given voting, + // then they vote for that list. + it('delegate can manage several voters and vote for all (voteFor)', async () => { + const delegateList= [ [delegate1, holder1], [delegate1, holder20] ] + + for (const [delegate, holder] of delegateList) { + await setDelegate(delegate, holder) + } + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [holder1,holder20]) + + for (const holder of delegatedVotersData[0]) { + await attemptVoteFor(voteId, false, holder, delegate1) + } + + await verifyVoteYN(voteId, 0, LDO1.add(LDO20)) + const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) + }) + + // Multiple voters with non-zero balances of governance token are delegating their voting + // power to a single delegate. The voting starts and the delegate is voting for all of them - first + // they get the full list of voters with their balance snapshot and vote states of the given voting, + // then they vote for that list. + it('delegate can manage several voters and vote all (voteForMulti)', async () => { + const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] + for (const [delegate, holder] of delegateList) { + await setDelegate(delegate, holder) + } + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) + + assertArraysEqualAsSets(delegatedVotersData[0], [holder29,holder51]) + + await attemptVoteForMultiple(voteId, true, delegatedVotersData[0], delegate2) + await verifyVoteYN(voteId, LDO51.add(LDO29), 0) + + const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) + }) + + // Multiple voters with non-zero balances of governance token are delegating their voting + // power to a single delegate. The voting starts and the delegate is voting for one of them. + it('delegate can manage several voters and vote for first (voteFor)', async () => { + const delegateList= [ [delegate1, holder1], [delegate1, holder20] ] + + for (const [delegate, holder] of delegateList) { + await setDelegate(delegate, holder) + } + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [holder1, holder20]) + + await attemptVoteFor(voteId, false, holder1, delegate1) + + await verifyVoteYN(voteId, 0, LDO1) + + const voterStateHolder1 = await voting.getVotersStateAtVote(voteId, [holder1]) + const voterStateHolder20 = await voting.getVotersStateAtVote(voteId, [holder20]) + + assertArraysEqualAsSets(voterStateHolder1, [VOTER_STATE.DELEGATE_NAY.toString()]) + assertArraysEqualAsSets(voterStateHolder20, [VOTER_STATE.ABSENT.toString()]) + }) + + // Multiple voters with non-zero balances of governance token are delegating their voting + // power to a single delegate. The voting starts and the delegate is voting for one of them. + it('delegate can manage several voters and vote for first (voteForMulti)', async () => { + const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] + for (const [delegate, holder] of delegateList) { + setDelegate(delegate, holder) + } + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) + + assertArraysEqualAsSets(delegatedVotersData[0], [ holder29, holder51 ]) + + await attemptVoteForMultiple(voteId, true, [holder29], delegate2) + + await verifyVoteYN(voteId, LDO29, 0) + + const voterStateHolder29 = await voting.getVotersStateAtVote(voteId, [holder29]) + const voterStateHolder51 = await voting.getVotersStateAtVote(voteId, [holder51]) + + assertArraysEqualAsSets(voterStateHolder29, [VOTER_STATE.DELEGATE_YEA.toString()]) + assertArraysEqualAsSets(voterStateHolder51, [VOTER_STATE.ABSENT.toString()]) + }) + + // A delegated voter can overwrite a delegate's vote. + it('delegated voter can overwrite a delegates vote (voteFor)', async () => { + const [ delegate, holder] = [ delegate1, holder1 ] + + setDelegate(delegate, holder) + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [ holder1 ]) + + const supports = false + await attemptVoteFor(voteId, supports, holder, delegate1) + + const delegatedVoterState = await voting.getVotersStateAtVote(voteId, [holder]) + assertArraysEqualAsSets(delegatedVoterState, [VOTER_STATE.DELEGATE_NAY.toString()]) + + await vote( voteId, !supports, false, holder) + + await verifyVoteYN(voteId, LDO1, 0) + + const voterState = await voting.getVotersStateAtVote(voteId, [holder]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.YEA.toString()]) + }) + + // A delegated voter can overwrite a delegate's vote. + it('delegated voter can overwrite a delegates vote (voteForMulti)', async () => { + const [ delegate, holder] = [ delegate2, holder29 ] + + await setDelegate(delegate, holder) + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [holder29]) + + const supports = true + await attemptVoteForMultiple(voteId, supports, [holder], delegate2) + const delegatedVoterState = await voting.getVotersStateAtVote(voteId, [holder]) + assertArraysEqualAsSets(delegatedVoterState, [VOTER_STATE.DELEGATE_YEA.toString()]) + + await vote(voteId, !supports, false, holder) + + await verifyVoteYN(voteId, 0 , LDO29) + + const voterState = await voting.getVotersStateAtVote(voteId, [holder]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.NAY.toString()]) + }) + + // A delegate can vote for a voter that delegated them their voting power during the active + // phase of the vote. + it('delegate can vote for a voter that delegated them their voting power during the active phase (voteFor)', async () => { + const [ delegate, holder] = [ delegate2, holder1 ] + + await setDelegate(delegate, holder) + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + const { phase } = await voting.getVote(voteId) + assert.equal(phase, MAIN_PHASE) + + await setDelegate(delegate1, holder) + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [ holder1 ]) + + await attemptVoteFor(voteId, false, holder, delegate1) + + await verifyVoteYN(voteId, 0, LDO1) + + const voterState = await voting.getVotersStateAtVote(voteId, [holder]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) + }) + + // A delegate can vote for a voter that delegated them their voting power during the active + // phase of the vote. + it('delegate can vote for a voter that delegated them their voting power during the active phase (voteForMulti)', async () => { + const [ delegate, holder] = [ delegate1, holder29 ] + + await setDelegate(delegate, holder) + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + const { phase } = await voting.getVote(voteId) + assert.equal(phase, MAIN_PHASE) + + await setDelegate(delegate2, holder) + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [holder29]) + + await attemptVoteForMultiple(voteId, true, [holder], delegate2) + + await verifyVoteYN(voteId, LDO29, 0) + + const voterState = await voting.getVotersStateAtVote(voteId, [holder]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) + }) + + // A delegate can't vote for a voter that acquired voting power during the active phase of the vote. + it('delegate cant vote for a voter that acquired voting power during the active phase of the vote (voteFor)', async () => { + const [ delegate, holder] = [ delegate1, holder1 ] + + await setDelegate(delegate, holder) + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + const { phase } = await voting.getVote(voteId) + assert.equal(phase, MAIN_PHASE) + + await setDelegate(delegate2, holder) + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [ ]) + + await assertRevert(voting.attemptVoteFor(voteId, false, holder, {from: delegate1}), ERRORS.VOTING_CAN_NOT_VOTE_FOR) + }) + + // A delegate can't vote for a voter that acquired voting power during the active phase of the vote. + it('delegate cant vote for a voter that acquired voting power during the active phase of the vote (voteForMulti)', async () => { + const [ delegate, holder] = [ delegate2, holder29 ] + + await setDelegate(delegate, holder) + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + const { phase } = await voting.getVote(voteId) + assert.equal(phase, MAIN_PHASE) + + await setDelegate(delegate1, holder) + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], []) + + await assertRevert(voting.attemptVoteForMultiple(voteId, true, [holder], {from: delegate2}), ERRORS.VOTING_CAN_NOT_VOTE_FOR) + + }) + + // If a delegated voter lost or gain some voting power after the start of the vote, a delegate + // would still apply the full voting power of the delegated voter (at the vote's snapshot) + it('delegate vote by snapshot vp not current (voteFor)', async () => { + const delegateList= [ [delegate1, holder1], [delegate1, holder20] ] + + for (const [delegate, holder] of delegateList) { + await setDelegate(delegate, holder) + } + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + await token.generateTokens(holder1, bigExp(2, decimals)) + await token.destroyTokens(holder20, bigExp(5, decimals)) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [holder1,holder20]) + + for (const holder of delegatedVotersData[0]) { + await attemptVoteFor(voteId, false, holder, delegate1) + } + await verifyVoteYN(voteId, 0, LDO1.add(LDO20)) + + const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) + }) + + // If a delegated voter lost or gain some voting power after the start of the vote, a delegate + // would still apply the full voting power of the delegated voter (at the vote's snapshot) + it('delegate vote by snapshot vp not current (voteForMulti)', async () => { + const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] + for (const [delegate, holder] of delegateList) { + await setDelegate(delegate, holder) + } + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + await token.generateTokens(holder29, bigExp(2, decimals)) + await token.destroyTokens(holder51, bigExp(5, decimals)) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [holder29,holder51]) + + await attemptVoteForMultiple(voteId, true, delegatedVotersData[0], delegate2) + await verifyVoteYN(voteId, LDO51.add(LDO29), 0) + + const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) + }) + + // A delegate can vote for all their delegated voters even if delegate voted for them before + // for different option (change mind) + it('delegate change mind (voteFor)', async () => { + const delegateList = [[delegate1, holder1], [delegate1, holder20]] + + for (const [delegate, holder] of delegateList) { + await setDelegate(delegate, holder) + } + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) + + assertArraysEqualAsSets(delegatedVotersData[0], [holder1, holder20]) + + for (const holder of delegatedVotersData[0]) { + await attemptVoteFor(voteId, false, holder, delegate1) + } + await verifyVoteYN(voteId,0, LDO1.add(LDO20)) + + for (const holder of delegatedVotersData[0]) { + await attemptVoteFor(voteId, true, holder, delegate1) + } + await verifyVoteYN(voteId, LDO1.add(LDO20), 0) + + const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) + }) + + // A delegate can vote for all their delegated voters even if delegate voted for them before + // for different option (change mind) + it('delegate change mind (voteForMulti)', async () => { + const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] + for (const [delegate, holder] of delegateList) { + await setDelegate(delegate, holder) + } + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [holder29,holder51]) + + + await attemptVoteForMultiple(voteId, true, delegatedVotersData[0], delegate2) + await verifyVoteYN(voteId, LDO51.add(LDO29), 0) + + await attemptVoteForMultiple(voteId, false, delegatedVotersData[0], delegate2) + await verifyVoteYN(voteId, 0, LDO51.add(LDO29)) + + const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) + }) + + // A delegate can vote for all their delegated voters even if delegate voted for them before + // for "no" option during the objection phase.(change mind) + it('delegate vote "yes" in main phase, delegate vote "no" in objection (voteFor)', async () => { + const delegateList = [[delegate1, holder1], [delegate1, holder20]] + + for (const [delegate, holder] of delegateList) { + await setDelegate(delegate, holder) + } + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [holder1, holder20]) + + for (const holder of delegatedVotersData[0]) { + await attemptVoteFor(voteId, true, holder, delegate1) + } + + await voting.mockIncreaseTime(votingDuration - objectionPhase) + await verifyVoteYN(voteId, LDO1.add(LDO20), 0) + + const { phase } = await voting.getVote(voteId) + assert.equal(phase, OBJECTION_PHASE) + + for (const holder of delegatedVotersData[0]) { + await attemptVoteFor(voteId, false, holder, delegate1) + } + await verifyVoteYN(voteId, 0, LDO1.add(LDO20)) + + const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) + }) + + // A delegate can vote for all their delegated voters even if delegate voted for them before + // for "no" option during the objection phase.(change mind) + it('delegate vote "yes" in main phase, delegate vote "no" in objection (voteForMulti)', async () => { + const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] + for (const [delegate, holder] of delegateList) { + await setDelegate(delegate, holder) + } + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [holder29,holder51]) + + await attemptVoteForMultiple(voteId, true, delegatedVotersData[0], delegate2) + + await voting.mockIncreaseTime(votingDuration - objectionPhase) + await verifyVoteYN(voteId, LDO51.add(LDO29), 0) + + const { phase } = await voting.getVote(voteId) + assert.equal(phase, OBJECTION_PHASE) + + await attemptVoteForMultiple(voteId, false, delegatedVotersData[0], delegate2) + + await verifyVoteYN(voteId, 0, LDO51.add(LDO29)) + + const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) + }) + + // A new delegate can vote "no" in objection phase for all their delegated voters + // even if old delegate voted for "yes" them before + it('delegate vote "yes" in main phase, new delegate vote "no" in objection (voteFor)', async () => { + const delegateList = [[delegate1, holder1], [delegate1, holder20]] + + for (const [delegate, holder] of delegateList) { + await setDelegate(delegate, holder) + } + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [holder1, holder20]) + + for (const holder of delegatedVotersData[0]) { + await attemptVoteFor(voteId, true, holder, delegate1) + } + + await voting.mockIncreaseTime(votingDuration - objectionPhase) + await verifyVoteYN(voteId, LDO1.add(LDO20), 0) + + const { phase } = await voting.getVote(voteId) + assert.equal(phase, OBJECTION_PHASE) + + const objectorList = [[delegate2, holder1], [delegate2, holder20]] + + for (const [delegate, holder] of objectorList) { + await setDelegate(delegate, holder) + } + + for (const [ delegate , holder] of objectorList) { + await attemptVoteFor(voteId, false, holder, delegate) + } + await verifyVoteYN(voteId, 0, LDO1.add(LDO20)) + + const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) + }) + + // A new delegate can vote "no" in objection phase for all their delegated voters + // even if old delegate voted for "yes" them before + it('delegate vote "yes" in main phase, new delegate vote "no" in objection (voteForMulti)', async () => { + const delegateList= [[delegate2, holder29], [delegate2, holder51]] + for (const [delegate, holder] of delegateList) { + await setDelegate(delegate, holder) + } + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [holder29, holder51]) + + await attemptVoteForMultiple(voteId, true, delegatedVotersData[0], delegate2) + + await voting.mockIncreaseTime(votingDuration - objectionPhase) + await verifyVoteYN(voteId, LDO51.add(LDO29), 0) + + const { phase } = await voting.getVote(voteId) + assert.equal(phase, OBJECTION_PHASE) + + const objectorList = [[delegate1, holder29], [delegate1, holder51]] + + for (const [delegate, holder] of objectorList) { + await setDelegate(delegate, holder) + } + + await attemptVoteForMultiple(voteId, false, delegatedVotersData[0], delegate1) + + await verifyVoteYN(voteId, 0, LDO51.add(LDO29)) + + const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) + }) + + // A delegate is voting "yea" at the last moment of a vote's main phase. Delegated voters + // should be able to overpower the delegate during the objection phase. + it('delegate vote in main phase, voter overpower in objection (voteFor)', async () => { + const delegateList = [[delegate1, holder1], [delegate1, holder20]] + + for (const [delegate, holder] of delegateList) { + await setDelegate(delegate, holder) + } + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [holder1, holder20]) + + for (const holder of delegatedVotersData[0]) { + await attemptVoteFor(voteId, true, holder, delegate1) + } + + await voting.mockIncreaseTime(votingDuration - objectionPhase) + await verifyVoteYN(voteId, LDO1.add(LDO20), 0) + + const { phase } = await voting.getVote(voteId) + assert.equal(phase, OBJECTION_PHASE) + + for (const holder of delegatedVotersData[0]) { + await vote(voteId, false, false, holder) + } + await verifyVoteYN(voteId, 0, LDO1.add(LDO20)) + + const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.NAY.toString()]) + }) + + // A delegate is voting "yea" at the last moment of a vote's main phase. Delegated voters + // should be able to overpower the delegate during the objection phase. + it('delegate vote in main phase, voter overpower in objection (voteForMulti)', async () => { + const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] + for (const [delegate, holder] of delegateList) { + await setDelegate(delegate, holder) + } + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [holder29,holder51]) + + await attemptVoteForMultiple(voteId, true, delegatedVotersData[0], delegate2) + + await voting.mockIncreaseTime(votingDuration - objectionPhase) + await verifyVoteYN(voteId, LDO51.add(LDO29), 0) + + const { phase } = await voting.getVote(voteId) + assert.equal(phase, OBJECTION_PHASE) + + for (const holder of delegatedVotersData[0]) { + await vote(voteId, false, false, holder) + } + await verifyVoteYN(voteId, 0, LDO51.add(LDO29)) + + const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.NAY.toString()]) + }) + + // If a delegate was spammed by a large amount of fake delegated voters, they can still easily + // retrieve an actual voters list and vote for that list. + it('delegate can vote after spam', async () => { + await setDelegate(delegate2, holder29) + for (const holder of spamHolders) { + await token.generateTokens(holder, LDO3) + await setDelegate(delegate2, holder) + } + await setDelegate(delegate2, holder51) + + const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 600, voteId) + assertArraysEqualAsSets(delegatedVotersData[0], [holder29, ...spamHolders, holder51]) + + await attemptVoteForMultiple(voteId, true, [holder29, holder51], delegate2) + + await verifyVoteYN(voteId, LDO51.add(LDO29), 0) + + const voterState = await voting.getVotersStateAtVote(voteId, [holder29, holder51]) + assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) + + const voterStateSpam = await voting.getVotersStateAtVote(voteId, spamHolders) + assertArraysEqualAsSets(voterStateSpam, [VOTER_STATE.ABSENT.toString()]) + }).timeout(60_000); + + ///end + }) + + context('delegated voters getters', () => { + const neededSupport = pct16(50) + const minimumAcceptanceQuorum = pct16(20) + const decimals = 18 + const defaultLimit = 100 + const voters = [{ + account: holder1, + balance: bigExp(1, decimals) + },{ + account: holder2, + balance: bigExp(2, decimals) + }, { + account: holder20, + balance: bigExp(20, decimals) + }, { + account: holder29, + balance: bigExp(29, decimals) + }] + + beforeEach(async () => { + token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', decimals, 'n', true) // empty parameters minime + + await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, 0) + + for (let i = 0; i < voters.length; i++) { + await token.generateTokens(voters[i].account, voters[i].balance) + await voting.setDelegate(delegate1, {from: voters[i].account}) + } + + executionTarget = await ExecutionTarget.new() + }) + + it('should return correct delegated voters count', async () => { + const delegatedVotersCount = (await voting.getDelegatedVotersCount(delegate1)).toNumber() + assert(delegatedVotersCount === voters.length) + }) + + it(`revert if "_delegate" is zero address`, async () => { + await assertRevert( + voting.getDelegatedVotersCount(ZERO_ADDRESS), + ERRORS.VOTING_ZERO_ADDRESS_PASSED + ) + + await assertRevert( + voting.getDelegatedVoters(ZERO_ADDRESS, 0, defaultLimit), + ERRORS.VOTING_ZERO_ADDRESS_PASSED + ) + }) + + it(`revert if "_limit" is 0`, async () => { + await assertRevert( + voting.getDelegatedVoters(ZERO_ADDRESS, 0, defaultLimit), + ERRORS.VOTING_ZERO_ADDRESS_PASSED + ) + }) + + it(`if delegatedVoters array length is 0, return two empty arrays`, async () => { + const delegatdVotersData = await voting.getDelegatedVoters(nonHolder, 0, defaultLimit) + assert(delegatdVotersData[0].length === 0, 'votersList should be empty') + assert(delegatdVotersData[1].length === 0, 'votingPowerList should be empty') + }) + + it(`should return correct delegated voters data if offset + limit >= votersCount`, async () => { + const offset = 2 + const limit = 5 + const delegatedVotersData = await voting.getDelegatedVoters(delegate1, offset, limit) + const delegatedVotersCount = (await voting.getDelegatedVotersCount(delegate1)).toNumber() + const delegatedVotersCountToReturn = delegatedVotersCount - offset + + assert(delegatedVotersData[0].length === delegatedVotersCountToReturn) + assert(delegatedVotersData[1].length === delegatedVotersCountToReturn) + + const votersSlice = voters.slice(offset, delegatedVotersCount) + const votersListSlice = votersSlice.map(voter => voter.account) + assertArraysEqualAsSets(delegatedVotersData[0], votersListSlice, 'votersList should be correct') + + const votingPowerListSlice = votersSlice.map((voter) => voter.balance.toString()) + const votingPowerList = delegatedVotersData[1].map(votingPower => votingPower.toString()) + assertArraysEqualAsSets(votingPowerList, votingPowerListSlice, 'votingPowerList should be correct') + }) + + it(`should return correct delegated voters data if offset + limit < votersCount`, async () => { + const offset = 1 + const limit = 1 + const delegatedVotersData = await voting.getDelegatedVoters(delegate1, offset, limit) + + assert(delegatedVotersData[0].length === limit) + assert(delegatedVotersData[1].length === limit) + + const votersSlice = voters.slice(offset, offset + limit) + const votersListSlice = votersSlice.map(voter => voter.account) + assertArraysEqualAsSets(delegatedVotersData[0], votersListSlice, 'votersList should be correct') + + const votingPowerListSlice = votersSlice.map((voter) => voter.balance.toString()) + const votingPowerList = delegatedVotersData[1].map(votingPower => votingPower.toString()) + assertArraysEqualAsSets(votingPowerList, votingPowerListSlice, 'votingPowerList should be correct') + }) + + it(`revert if _voter is zero address`, async () => { + await assertRevert( + voting.getDelegate(ZERO_ADDRESS), + ERRORS.VOTING_ZERO_ADDRESS_PASSED + ) + }) + + it(`return zero address if no delegate`, async () => { + const delegate = await voting.getDelegate(nonHolder) + assert.equal(delegate, ZERO_ADDRESS, 'should return zero address') + }) + + it(`can get voter's delegate address`, async () => { + const delegate = await voting.getDelegate(holder1) + assert.equal(delegate, delegate1, 'should return delegate1 address') + }) + }) + + context('voting as delegate', () => { + let script, voteId, creator, metadata + + const neededSupport = pct16(50) + const minimumAcceptanceQuorum = pct16(20) + const decimals = 18 + const voters = [{ + account: holder1, + balance: bigExp(1, decimals) + }, { + account: holder2, + balance: bigExp(2, decimals) + }, { + account: holder20, + balance: bigExp(20, decimals) + }, { + account: holder29, + balance: bigExp(29, decimals) + }, { + account: holder51, + balance: bigExp(51, decimals) + }] + + beforeEach(async () => { + token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', decimals, 'n', true) // empty parameters minime + + await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, objectionPhase) + + for (let i = 0; i < voters.length; i++) { + await token.generateTokens(voters[i].account, voters[i].balance) + await voting.setDelegate(delegate1, {from: voters[i].account}) + } + + executionTarget = await ExecutionTarget.new() + + const action = {to: executionTarget.address, calldata: executionTarget.contract.methods.execute().encodeABI()} + script = encodeCallScript([action, action]) + + await voting.resetDelegate({from: holder1}) + await token.transfer(holder51, bigExp(2, decimals), { from: holder2 }) + + const receipt = await voting.methods['newVote(bytes,string)'](script, 'metadata', {from: holder51}); + voteId = getEventArgument(receipt, 'StartVote', 'voteId') + creator = getEventArgument(receipt, 'StartVote', 'creator') + metadata = getEventArgument(receipt, 'StartVote', 'metadata') + }) + + it(`getVotersStateAtVote`, async () => { + await voting.vote(voteId, true, false, { from: holder20 }) + await voting.vote(voteId, false, false, { from: holder29 }) + await voting.attemptVoteForMultiple(voteId, false, [holder51], {from: delegate1}) + + const votersState = await voting.getVotersStateAtVote(voteId, [holder20, holder29, holder51]) + assert.equal(votersState[0], VOTER_STATE.YEA, `holder20 should have 'yea' state`) + assert.equal(votersState[1], VOTER_STATE.NAY, `holder29 should have 'nay' state`) + assert.equal(votersState[2], VOTER_STATE.DELEGATE_NAY, `holder51 should have 'delegateNay' state`) + }) + + it(`revert if vote does not exist`, async () => { + await assertRevert( + voting.attemptVoteForMultiple(voteId + 1, false, [holder51], {from: delegate1}), + ERRORS.VOTING_NO_VOTE + ) + }) + + it(`revert if vote has already ended`, async () => { + await voting.mockIncreaseTime(votingDuration + 1) + await assertRevert( + voting.attemptVoteForMultiple(voteId, false, [holder51], {from: delegate1}), + ERRORS.VOTING_CAN_NOT_VOTE + ) + }) + + it(`revert if vote has already been executed`, async () => { + await voting.vote(voteId, true, true, { from: holder51 }) + await voting.mockIncreaseTime(votingDuration + 1) + await voting.executeVote(voteId) + + await assertRevert( + voting.attemptVoteForMultiple(voteId, true, [holder51], {from: delegate1}), + ERRORS.VOTING_CAN_NOT_VOTE + ) + }) + + it(`revert if trying to vote 'yea' during objection phase`, async () => { + await voting.mockIncreaseTime(mainPhase + 200) + await assertRevert( + voting.attemptVoteForMultiple(voteId, true, [holder51], {from: delegate1}), + ERRORS.VOTING_CAN_NOT_VOTE + ) + }) + + it(`revert if one of the voters has 0 LDO`, async () => { + // holder 2 has 0 LDO + await assertRevert( + voting.attemptVoteForMultiple(voteId, true, [holder51, holder2, holder1], {from: delegate1}), + ERRORS.VOTING_NO_VOTING_POWER + ) + }) + + it(`one of the voters voted beforehand`, async () => { + await voting.vote(voteId, false, false, { from: holder29 }) + const tx = await voting.attemptVoteForMultiple(voteId, true, [holder20, holder29], {from: delegate1}); + + assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 1}) + assertEvent(tx, 'CastVote', {expectedArgs: {voteId, voter: holder20, supports: true}}) + assertEvent(tx, 'CastVoteAsDelegate', {expectedArgs: {voteId, delegate: delegate1, voter: holder20, supports: true}}) + + assert.equal(await voting.getVoterState(voteId, holder29), VOTER_STATE.NAY, `holder29 should have 'nay' state`) + assert.equal(await voting.getVoterState(voteId, holder20), VOTER_STATE.DELEGATE_YEA, `holder20 should have 'delegateYea' state`) + }) + + it(`vote for multiple with duplicates`, async () => { + const tx = await voting.attemptVoteForMultiple(voteId, false, [holder29, holder29], {from: delegate1}); + + assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 2}) + assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 2}) + assert.equal(await voting.getVoterState(voteId, holder29), VOTER_STATE.DELEGATE_NAY, `holder29 should have 'delegateNay' state`) + const vote = await voting.getVote(voteId) + assertBn(vote.nay, bigExp(29, decimals), 'nay should be 29') + }) + + it(`vote for empty list`, async () => { + await assertRevert( + voting.attemptVoteForMultiple(voteId, false, [], {from: delegate1}), + ERRORS.VOTING_CAN_NOT_VOTE_FOR + ) + }) + + it(`skipped vote for multiple for all voters from list`, async () => { + await voting.vote(voteId, false, false, { from: holder20 }) + await voting.vote(voteId, false, false, { from: holder29 }) + await assertRevert( + voting.attemptVoteForMultiple(voteId, false, [holder20, holder29], {from: delegate1}), + ERRORS.VOTING_CAN_NOT_VOTE_FOR + ) + }) + + it(`successful vote for multiple`, async () => { + const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 100, voteId) + const delegatedVotersAddresses = delegatedVotersData[0] + const delegatedVotersVotingPower = delegatedVotersData[1] + const filteredDelegatedVoters = [] + for (let i = 0; i < delegatedVotersAddresses.length; i++) { + const votingPower = delegatedVotersVotingPower[i] + if (votingPower.gt(bigExp(0, decimals))) { + filteredDelegatedVoters.push({address: delegatedVotersAddresses[i], votingPower}) + } + } + const filteredDelegatedVotersAddresses = filteredDelegatedVoters.map(({address}) => address) + const tx = await voting.attemptVoteForMultiple( + voteId, + false, + filteredDelegatedVotersAddresses, + {from: delegate1} + ); + + // Check amount of events + assertAmountOfEvents(tx, 'CastVote', {expectedAmount: filteredDelegatedVoters.length}) + assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: filteredDelegatedVoters.length}) + + // Check events content + for (let i = 0; i < filteredDelegatedVoters.length; i++) { + const {address, votingPower} = filteredDelegatedVoters[i] + assertEvent(tx, 'CastVote', {index: i, expectedArgs: {voteId, voter: address, supports: false, stake: votingPower}}) + assertEvent(tx, 'CastVoteAsDelegate', {index: i, expectedArgs: {voteId, delegate: delegate1, voter: address, supports: false, stake: votingPower}}) + } + + // Check voters' state + const votersState = await voting.getVotersStateAtVote(voteId, filteredDelegatedVotersAddresses) + votersState.every((state) => { + assert.equal(state, VOTER_STATE.DELEGATE_NAY.toString(), `voter should have 'delegateNay' state`) + }) + + // Check applied VP + const vote = await voting.getVote(voteId) + const votingPowerSum = filteredDelegatedVoters.reduce( + (sum, {votingPower}) => sum.add(votingPower), + bigExp(0, decimals) + ) + + assertBn(vote.yea, bigExp(0, decimals), 'yea should be 0') + assertBn(vote.nay, votingPowerSum, 'nay should be sum of all VP') + }) + + it(`successful vote for single`, async () => { + const tx = await voting.attemptVoteFor(voteId, false, holder29, {from: delegate1}) + + // Check amount of events + assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 1}) + + const holder29VP = bigExp(29, decimals) + + // Check events content + assertEvent(tx, 'CastVote', { expectedArgs: {voteId, voter: holder29, supports: false, stake: holder29VP}}) + assertEvent(tx, 'CastVoteAsDelegate', { expectedArgs: {voteId, delegate: delegate1, voter: holder29, supports: false, stake: holder29VP}}) + + + // Check voter's state + assert.equal(await voting.getVoterState(voteId, holder29), VOTER_STATE.DELEGATE_NAY, `holder29 should have 'delegateNay' state`) + + // Check applied VP + const vote = await voting.getVote(voteId) + assertBn(vote.yea, bigExp(0, decimals), 'yea should be 0') + assertBn(vote.nay, holder29VP, 'nay should be holder29 VP') + }) + + }) +}) diff --git a/apps/voting/test/voting.js b/apps/voting/test/voting.js index 66def0e0f..b31c6ec83 100644 --- a/apps/voting/test/voting.js +++ b/apps/voting/test/voting.js @@ -12,12 +12,12 @@ const ExecutionTarget = artifacts.require('ExecutionTarget') const createdVoteId = receipt => getEventArgument(receipt, 'StartVote', 'voteId') -const VOTER_STATE = ['ABSENT', 'YEA', 'NAY', 'DELEGATE_YEA', 'DELEGATE_NAY'].reduce((state, key, index) => { +const VOTER_STATE = ['ABSENT', 'YEA', 'NAY'].reduce((state, key, index) => { state[key] = index; return state; }, {}) -contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, delegate1, delegate2, nonHolder, ...spamHolders]) => { +contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, nonHolder]) => { let votingBase, voting, token, executionTarget, aclP let CREATE_VOTES_ROLE, MODIFY_SUPPORT_ROLE, MODIFY_QUORUM_ROLE, UNSAFELY_MODIFY_VOTE_TIME_ROLE @@ -794,1179 +794,4 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, d assert.isTrue(voteState[1], 'vite should be executed') }) }) - - context('voting delegate', () => { - const neededSupport = pct16(50) - const minimumAcceptanceQuorum = pct16(20) - const decimals = 18 - - const MAIN_PHASE = 0 - const OBJECTION_PHASE = 1 - - const LDO1 = bigExp(1, decimals) - const LDO20 = bigExp(20, decimals) - const LDO29 = bigExp(29, decimals) - const LDO51 = bigExp(51, decimals) - const LDO3 = bigExp(3, decimals) - const initBalance = { - [holder1]: LDO1, - [holder20]: LDO20, - [holder29]: LDO29, - [holder51]: LDO51, - } - - const setDelegate = async (delegate, holder ) => { - const tx = await voting.setDelegate(delegate, {from: holder}) - assertEvent(tx, 'SetDelegate', { - expectedArgs: {voter: holder, delegate} - }) - assertAmountOfEvents(tx, 'SetDelegate', {expectedAmount: 1}) - } - const attemptVoteFor = async (voteId, supports, holder, delegate) => { - const tx = await voting.attemptVoteFor(voteId, supports, holder, {from: delegate}) - assertEvent(tx, 'CastVote', { - expectedArgs: {voteId: voteId, voter: holder, supports: supports, stake: initBalance[holder]} - }) - assertEvent(tx, 'CastVoteAsDelegate', { - expectedArgs: {voteId: voteId, delegate: delegate, voter: holder, supports: supports, stake: initBalance[holder]} - }) - assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) - assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 1}) - } - const attemptVoteForMultiple = async (voteId, supports, holders, delegate) => { - const tx = await voting.attemptVoteForMultiple(voteId, supports, holders, {from: delegate}) - - for (const index of Object.keys(holders)){ - const holder = holders[index] - let stake - if (initBalance[holder]) { - stake = initBalance[holder] - } - if (!stake && spamHolders.includes(holder)) { - stake = LDO3 - } - assertEvent(tx, 'CastVote', { - index, - expectedArgs: {voteId, voter: holder, supports, stake} - }) - assertEvent(tx, 'CastVoteAsDelegate', { - index, - expectedArgs: {voteId, delegate, voter: holder, supports, stake} - }) - } - assertAmountOfEvents(tx, 'CastVote', {expectedAmount: holders.length}) - assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: holders.length}) - } - const vote = async (voteId, supports, exec, holder) => { - const tx = await voting.vote(voteId, supports, exec, {from: holder}) - assertEvent(tx, 'CastVote', { - expectedArgs: {voteId: voteId, voter: holder, supports, stake: initBalance[holder]} - }) - assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) - } - const verifyVoteYN = async (voteId, yes, no) => { - const { yea, nay } = await voting.getVote(voteId) - - assert.equal(yea.toString(), yes.toString()) - assert.equal(nay.toString(), no.toString()) - } - - beforeEach(async () => { - token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', decimals, 'n', true) // empty parameters minime - for (const [holder, balance] of Object.entries(initBalance)){ - await token.generateTokens(holder, balance) - } - - await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, objectionPhase) - - executionTarget = await ExecutionTarget.new() - }) - - it('voter can set delegate', async () => { - const tx = await voting.setDelegate(delegate1, {from: holder29}) - assertEvent(tx, 'SetDelegate', { - expectedArgs: {voter: holder29, delegate: delegate1} - }) - assertAmountOfEvents(tx, 'SetDelegate', {expectedAmount: 1}) - - const delegate = await voting.getDelegate(holder29) - assert.equal(delegate, delegate1, 'holder29 should have delegate1 as a delegate') - - const delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] - assertArraysEqualAsSets(delegatedVoters, [holder29], 'delegate1 should be a delegate of holder29') - }) - - it('voter can remove delegate', async () => { - await voting.setDelegate(delegate1, {from: holder29}) - - const tx = await voting.resetDelegate({from: holder29}) - assertEvent(tx, 'ResetDelegate', { - expectedArgs: {voter: holder29, delegate: delegate1} - }) - assertAmountOfEvents(tx, 'ResetDelegate', {expectedAmount: 1}) - const delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] - assertArraysEqualAsSets(delegatedVoters, [], 'delegate1 should not be a delegate of anyone') - }) - - it('voters can remove delegate', async () => { - await voting.setDelegate(delegate1, {from: holder20}) - await voting.setDelegate(delegate1, {from: holder29}) - await voting.setDelegate(delegate1, {from: holder51}) - - - const tx1 = await voting.resetDelegate({from: holder29}) - assertEvent(tx1, 'ResetDelegate', { - expectedArgs: {voter: holder29, delegate: delegate1} - }) - assertAmountOfEvents(tx1, 'ResetDelegate', {expectedAmount: 1}) - const tx2 = await voting.resetDelegate({from: holder51}) - assertEvent(tx2, 'ResetDelegate', { - expectedArgs: {voter: holder51, delegate: delegate1} - }) - assertAmountOfEvents(tx2, 'ResetDelegate', {expectedAmount: 1}) - const delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] - assertArraysEqualAsSets(delegatedVoters, [holder20], 'delegate1 have only holder20 as a delegated voter') - }) - - it('voter can change delegate', async () => { - await voting.setDelegate(delegate1, {from: holder29}) - await voting.setDelegate(delegate2, {from: holder51}) - - await voting.setDelegate(delegate2, {from: holder29}) - - const delegatedVotersDelegate1 = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] - assertArraysEqualAsSets(delegatedVotersDelegate1, [], 'delegate1 should not be a delegate of anyone') - const delegatedVotersDelegate2 = (await voting.getDelegatedVoters(delegate2, 0, 2))[0] - assertArraysEqualAsSets(delegatedVotersDelegate2, [holder29, holder51], 'delegate2 should be a delegate of holder29 and holder51') - }) - - it('delegate can manage several voters', async () => { - await voting.setDelegate(delegate1, {from: holder29}) - - const tx = await voting.setDelegate(delegate1, {from: holder51}) - assertEvent(tx, 'SetDelegate', { - expectedArgs: {voter: holder51, delegate: delegate1} - }) - assertAmountOfEvents(tx, 'SetDelegate', {expectedAmount: 1}) - - const delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 2))[0] - assertArraysEqualAsSets(delegatedVoters, [holder29, holder51], 'delegate1 should be a delegate of holder29 and holder51') - }) - - // Multiple voters with non-zero balances of governance token are delegating their voting - // power to a single delegate. The voting starts and the delegate is voting for all of them - first - // they get the full list of voters with their balance snapshot and vote states of the given voting, - // then they vote for that list. - it('delegate can manage several voters and vote for all (voteFor)', async () => { - const delegateList= [ [delegate1, holder1], [delegate1, holder20] ] - - for (const [delegate, holder] of delegateList) { - setDelegate(delegate, holder) - } - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [holder1,holder20]) - - for (const holder of delegatedVotersData[0]) { - await attemptVoteFor(voteId, false, holder, delegate1) - } - - await verifyVoteYN(voteId, 0, LDO1.add(LDO20)) - const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) - assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) - }) - - // Multiple voters with non-zero balances of governance token are delegating their voting - // power to a single delegate. The voting starts and the delegate is voting for all of them - first - // they get the full list of voters with their balance snapshot and vote states of the given voting, - // then they vote for that list. - it('delegate can manage several voters and vote all (voteForMulti)', async () => { - const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] - for (const [delegate, holder] of delegateList) { - await setDelegate(delegate, holder) - } - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) - - assertArraysEqualAsSets(delegatedVotersData[0], [holder29,holder51]) - - await attemptVoteForMultiple(voteId, true, delegatedVotersData[0], delegate2) - await verifyVoteYN(voteId, LDO51.add(LDO29), 0) - - const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) - assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) - }) - - // Multiple voters with non-zero balances of governance token are delegating their voting - // power to a single delegate. The voting starts and the delegate is voting for one of them. - it('delegate can manage several voters and vote for first (voteFor)', async () => { - const delegateList= [ [delegate1, holder1], [delegate1, holder20] ] - - for (const [delegate, holder] of delegateList) { - setDelegate(delegate, holder) - } - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [holder1, holder20]) - - const holder = delegatedVotersData[0][0] - await attemptVoteFor(voteId, false, holder, delegate1) - - await verifyVoteYN(voteId, 0, LDO1) - - const voterState = await voting.getVotersStateAtVote(voteId, [holder]) - - assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) - }) - - // Multiple voters with non-zero balances of governance token are delegating their voting - // power to a single delegate. The voting starts and the delegate is voting for one of them. - it('delegate can manage several voters and vote for first (voteForMulti)', async () => { - const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] - for (const [delegate, holder] of delegateList) { - setDelegate(delegate, holder) - } - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) - - assertArraysEqualAsSets(delegatedVotersData[0], [ holder29, holder51 ]) - - const holder = delegatedVotersData[0][0] - await attemptVoteForMultiple(voteId, true, [holder], delegate2) - - await verifyVoteYN(voteId, LDO29, 0) - - const voterState = await voting.getVotersStateAtVote(voteId, [holder]) - assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) - }) - - // A delegated voter can overwrite a delegate's vote. - it('delegated voter can overwrite a delegates vote (voteFor)', async () => { - const [ delegate, holder] = [ delegate1, holder1 ] - - setDelegate(delegate, holder) - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [ holder1 ]) - - const supports = false - await attemptVoteFor(voteId, supports, holder, delegate1) - await vote( voteId, !supports, false, holder) - - await verifyVoteYN(voteId, LDO1, 0) - - const voterState = await voting.getVotersStateAtVote(voteId, [holder]) - assertArraysEqualAsSets(voterState, [VOTER_STATE.YEA.toString()]) - }) - - // A delegated voter can overwrite a delegate's vote. - it('delegated voter can overwrite a delegates vote (voteForMulti)', async () => { - const [ delegate, holder] = [ delegate2, holder29 ] - - await setDelegate(delegate, holder) - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [holder29]) - - const supports = true - await attemptVoteForMultiple(voteId, supports, [holder], delegate2) - await vote(voteId, !supports, false, holder) - - await verifyVoteYN(voteId, 0 , LDO29) - - const voterState = await voting.getVotersStateAtVote(voteId, [holder]) - assertArraysEqualAsSets(voterState, [VOTER_STATE.NAY.toString()]) - }) - - // A delegate can vote for a voter that delegated them their voting power during the active - // phase of the vote. - it('delegate can vote for a voter that delegated them their voting power during the active phase (voteFor)', async () => { - const [ delegate, holder] = [ delegate2, holder1 ] - - await setDelegate(delegate, holder) - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - const { phase } = await voting.getVote(voteId) - assert.equal(phase, MAIN_PHASE) - - await setDelegate(delegate1, holder) - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [ holder1 ]) - - await attemptVoteFor(voteId, false, holder, delegate1) - - await verifyVoteYN(voteId, 0, LDO1) - - const voterState = await voting.getVotersStateAtVote(voteId, [holder]) - assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) - }) - - // A delegate can vote for a voter that delegated them their voting power during the active - // phase of the vote. - it('delegate can vote for a voter that delegated them their voting power during the active phase (voteForMulti)', async () => { - const [ delegate, holder] = [ delegate1, holder29 ] - - await setDelegate(delegate, holder) - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - const { phase } = await voting.getVote(voteId) - assert.equal(phase, MAIN_PHASE) - - await setDelegate(delegate2, holder) - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [holder29]) - - await attemptVoteForMultiple(voteId, true, [holder], delegate2) - - await verifyVoteYN(voteId, LDO29, 0) - - const voterState = await voting.getVotersStateAtVote(voteId, [holder]) - assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) - }) - - // A delegate can't vote for a voter that acquired voting power during the active phase of the vote. - it('delegate cant vote for a voter that acquired voting power during the active phase of the vote (voteFor)', async () => { - const [ delegate, holder] = [ delegate1, holder1 ] - - await setDelegate(delegate, holder) - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - const { phase } = await voting.getVote(voteId) - assert.equal(phase, MAIN_PHASE) - - await setDelegate(delegate2, holder) - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [ ]) - - await assertRevert(voting.attemptVoteFor(voteId, false, holder, {from: delegate1}), ERRORS.VOTING_CAN_NOT_VOTE_FOR) - }) - - // A delegate can't vote for a voter that acquired voting power during the active phase of the vote. - it('delegate cant vote for a voter that acquired voting power during the active phase of the vote (voteForMulti)', async () => { - const [ delegate, holder] = [ delegate2, holder29 ] - - await setDelegate(delegate, holder) - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - const { phase } = await voting.getVote(voteId) - assert.equal(phase, MAIN_PHASE) - - await setDelegate(delegate1, holder) - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], []) - - await assertRevert(voting.attemptVoteForMultiple(voteId, true, [holder], {from: delegate2}), ERRORS.VOTING_CAN_NOT_VOTE_FOR) - - }) - - // If a delegated voter lost or gain some voting power after the start of the vote, a delegate - // would still apply the full voting power of the delegated voter (at the vote's snapshot) - it('delegate vote by snapshot vp not current (voteFor)', async () => { - const delegateList= [ [delegate1, holder1], [delegate1, holder20] ] - - for (const [delegate, holder] of delegateList) { - await setDelegate(delegate, holder) - } - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - await token.generateTokens(holder1, bigExp(2, decimals)) - await token.destroyTokens(holder20, bigExp(5, decimals)) - - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [holder1,holder20]) - - for (const holder of delegatedVotersData[0]) { - await attemptVoteFor(voteId, false, holder, delegate1) - } - await verifyVoteYN(voteId, 0, LDO1.add(LDO20)) - - const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) - assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) - }) - - // If a delegated voter lost or gain some voting power after the start of the vote, a delegate - // would still apply the full voting power of the delegated voter (at the vote's snapshot) - it('delegate vote by snapshot vp not current (voteForMulti)', async () => { - const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] - for (const [delegate, holder] of delegateList) { - await setDelegate(delegate, holder) - } - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - await token.generateTokens(holder29, bigExp(2, decimals)) - await token.destroyTokens(holder51, bigExp(5, decimals)) - - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [holder29,holder51]) - - await attemptVoteForMultiple(voteId, true, delegatedVotersData[0], delegate2) - await verifyVoteYN(voteId, LDO51.add(LDO29), 0) - - const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) - assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) - }) - - // A delegate can vote for all their delegated voters even if delegate voted for them before - // for different option (change mind) - it('delegate change mind (voteFor)', async () => { - const delegateList = [[delegate1, holder1], [delegate1, holder20]] - - for (const [delegate, holder] of delegateList) { - await setDelegate(delegate, holder) - } - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) - - assertArraysEqualAsSets(delegatedVotersData[0], [holder1, holder20]) - - for (const holder of delegatedVotersData[0]) { - await attemptVoteFor(voteId, false, holder, delegate1) - } - await verifyVoteYN(voteId,0, LDO1.add(LDO20)) - - for (const holder of delegatedVotersData[0]) { - await attemptVoteFor(voteId, true, holder, delegate1) - } - await verifyVoteYN(voteId, LDO1.add(LDO20), 0) - - const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) - assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) - }) - - // A delegate can vote for all their delegated voters even if delegate voted for them before - // for different option (change mind) - it('delegate change mind (voteForMulti)', async () => { - const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] - for (const [delegate, holder] of delegateList) { - await setDelegate(delegate, holder) - } - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [holder29,holder51]) - - - await attemptVoteForMultiple(voteId, true, delegatedVotersData[0], delegate2) - await verifyVoteYN(voteId, LDO51.add(LDO29), 0) - - await attemptVoteForMultiple(voteId, false, delegatedVotersData[0], delegate2) - await verifyVoteYN(voteId, 0, LDO51.add(LDO29)) - - const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) - assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) - }) - - // A delegate can vote for all their delegated voters even if delegate voted for them before - // for "no" option during the objection phase.(change mind) - it('delegate vote "yes" in main phase, delegate vote "no" in objection (voteFor)', async () => { - const delegateList = [[delegate1, holder1], [delegate1, holder20]] - - for (const [delegate, holder] of delegateList) { - await setDelegate(delegate, holder) - } - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [holder1, holder20]) - - for (const holder of delegatedVotersData[0]) { - await attemptVoteFor(voteId, true, holder, delegate1) - } - - await voting.mockIncreaseTime(votingDuration - objectionPhase) - await verifyVoteYN(voteId, LDO1.add(LDO20), 0) - - const { phase } = await voting.getVote(voteId) - assert.equal(phase, OBJECTION_PHASE) - - for (const holder of delegatedVotersData[0]) { - await attemptVoteFor(voteId, false, holder, delegate1) - } - await verifyVoteYN(voteId, 0, LDO1.add(LDO20)) - - const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) - assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) - }) - - // A delegate can vote for all their delegated voters even if delegate voted for them before - // for "no" option during the objection phase.(change mind) - it('delegate vote "yes" in main phase, delegate vote "no" in objection (voteForMulti)', async () => { - const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] - for (const [delegate, holder] of delegateList) { - await setDelegate(delegate, holder) - } - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [holder29,holder51]) - - await attemptVoteForMultiple(voteId, true, delegatedVotersData[0], delegate2) - - await voting.mockIncreaseTime(votingDuration - objectionPhase) - await verifyVoteYN(voteId, LDO51.add(LDO29), 0) - - const { phase } = await voting.getVote(voteId) - assert.equal(phase, OBJECTION_PHASE) - - await attemptVoteForMultiple(voteId, false, delegatedVotersData[0], delegate2) - - await verifyVoteYN(voteId, 0, LDO51.add(LDO29)) - - const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) - assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) - }) - - // A new delegate can vote "no" in objection phase for all their delegated voters - // even if old delegate voted for "yes" them before - it('delegate vote "yes" in main phase, new delegate vote "no" in objection (voteFor)', async () => { - const delegateList = [[delegate1, holder1], [delegate1, holder20]] - - for (const [delegate, holder] of delegateList) { - await setDelegate(delegate, holder) - } - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [holder1, holder20]) - - for (const holder of delegatedVotersData[0]) { - await attemptVoteFor(voteId, true, holder, delegate1) - } - - await voting.mockIncreaseTime(votingDuration - objectionPhase) - await verifyVoteYN(voteId, LDO1.add(LDO20), 0) - - const { phase } = await voting.getVote(voteId) - assert.equal(phase, OBJECTION_PHASE) - - const objectorList = [[delegate2, holder1], [delegate2, holder20]] - - for (const [delegate, holder] of objectorList) { - await setDelegate(delegate, holder) - } - - for (const [ delegate , holder] of objectorList) { - await attemptVoteFor(voteId, false, holder, delegate) - } - await verifyVoteYN(voteId, 0, LDO1.add(LDO20)) - - const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) - assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) - }) - - // A new delegate can vote "no" in objection phase for all their delegated voters - // even if old delegate voted for "yes" them before - it('delegate vote "yes" in main phase, new delegate vote "no" in objection (voteForMulti)', async () => { - const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] - for (const [delegate, holder] of delegateList) { - await setDelegate(delegate, holder) - } - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [holder29,holder51]) - - await attemptVoteForMultiple(voteId, true, delegatedVotersData[0], delegate2) - - await voting.mockIncreaseTime(votingDuration - objectionPhase) - await verifyVoteYN(voteId, LDO51.add(LDO29), 0) - - const { phase } = await voting.getVote(voteId) - assert.equal(phase, OBJECTION_PHASE) - - const objectorList = [[delegate1, holder29], [delegate1, holder51]] - - for (const [delegate, holder] of objectorList) { - await setDelegate(delegate, holder) - } - - await attemptVoteForMultiple(voteId, false, delegatedVotersData[0], delegate1) - - await verifyVoteYN(voteId, 0, LDO51.add(LDO29)) - - const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) - assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) - }) - - // A delegate is voting "yea" at the last moment of a vote's main phase. Delegated voters - // should be able to overpower the delegate during the objection phase. - it('delegate vote in main phase, voter overpower in objection (voteFor)', async () => { - const delegateList = [[delegate1, holder1], [delegate1, holder20]] - - for (const [delegate, holder] of delegateList) { - await setDelegate(delegate, holder) - } - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [holder1, holder20]) - - for (const holder of delegatedVotersData[0]) { - await attemptVoteFor(voteId, true, holder, delegate1) - } - - await voting.mockIncreaseTime(votingDuration - objectionPhase) - await verifyVoteYN(voteId, LDO1.add(LDO20), 0) - - const { phase } = await voting.getVote(voteId) - assert.equal(phase, OBJECTION_PHASE) - - for (const holder of delegatedVotersData[0]) { - await vote(voteId, false, false, holder) - } - await verifyVoteYN(voteId, 0, LDO1.add(LDO20)) - - const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) - assertArraysEqualAsSets(voterState, [VOTER_STATE.NAY.toString()]) - }) - - // A delegate is voting "yea" at the last moment of a vote's main phase. Delegated voters - // should be able to overpower the delegate during the objection phase. - it('delegate vote in main phase, voter overpower in objection (voteForMulti)', async () => { - const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] - for (const [delegate, holder] of delegateList) { - await setDelegate(delegate, holder) - } - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [holder29,holder51]) - - await attemptVoteForMultiple(voteId, true, delegatedVotersData[0], delegate2) - - await voting.mockIncreaseTime(votingDuration - objectionPhase) - await verifyVoteYN(voteId, LDO51.add(LDO29), 0) - - const { phase } = await voting.getVote(voteId) - assert.equal(phase, OBJECTION_PHASE) - - for (const holder of delegatedVotersData[0]) { - await vote(voteId, false, false, holder) - } - await verifyVoteYN(voteId, 0, LDO51.add(LDO29)) - - const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) - assertArraysEqualAsSets(voterState, [VOTER_STATE.NAY.toString()]) - }) - // If a delegate was spammed by a large amount of fake delegated voters, they can still easily - // retrieve an actual voters list and vote for that list. - it('delegate can vote after spam', async () => { - await setDelegate(delegate2, holder29) - for (const holder of spamHolders) { - await token.generateTokens(holder, LDO3) - await setDelegate(delegate2, holder) - } - await setDelegate(delegate2, holder51) - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 600, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [holder29, ...spamHolders, holder51]) - - await attemptVoteForMultiple(voteId, true, [holder29, holder51], delegate2) - - await verifyVoteYN(voteId, LDO51.add(LDO29), 0) - - const voterState = await voting.getVotersStateAtVote(voteId, [holder29, holder51]) - assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) - - const voterStateSpam = await voting.getVotersStateAtVote(voteId, spamHolders) - assertArraysEqualAsSets(voterStateSpam, [VOTER_STATE.ABSENT.toString()]) - }).timeout(60_000); - - ///end - }) - - context('delegation state management', () => { - const neededSupport = pct16(50) - const minimumAcceptanceQuorum = pct16(20) - const decimals = 18 - const defaultLimit = 100 - - beforeEach(async () => { - token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', decimals, 'n', true) // empty parameters minime - - await token.generateTokens(holder20, bigExp(20, decimals)) - await token.generateTokens(holder29, bigExp(29, decimals)) - await token.generateTokens(holder51, bigExp(51, decimals)) - await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, 0) - - executionTarget = await ExecutionTarget.new() - }) - - it(`voter can assign themself a delegate`, async () => { - const tx = await voting.setDelegate(delegate1, {from: holder29}) - assertEvent(tx, 'SetDelegate', { - expectedArgs: {voter: holder29, delegate: delegate1} - }) - assertAmountOfEvents(tx, 'SetDelegate', {expectedAmount: 1}) - - const delegate = await voting.getDelegate(holder29) - assert.equal(delegate, delegate1, 'holder29 should have delegate1 as a delegate') - - const delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, defaultLimit))[0] - assertArraysEqualAsSets(delegatedVoters, [holder29], 'delegate1 should be a delegate of holder29') - }) - - it(`voter can change their assigned delegate`, async () => { - await voting.setDelegate(delegate1, {from: holder29}) - - const tx = await voting.setDelegate(delegate2, {from: holder29}) - assertEvent(tx, 'SetDelegate', { - expectedArgs: {voter: holder29, delegate: delegate2} - }) - assertAmountOfEvents(tx, 'SetDelegate', {expectedAmount: 1}) - - const delegate = await voting.getDelegate(holder29) - assert.equal(delegate, delegate2, 'holder29 should have delegate2 as a delegate') - - const delegatedVoters = (await voting.getDelegatedVoters(delegate2, 0, defaultLimit))[0] - assertArraysEqualAsSets(delegatedVoters, [holder29], 'delegate2 should be a delegate of holder29') - }) - - it(`multiple voters can assign themselves the delegate`, async () => { - await voting.setDelegate(delegate1, {from: holder29}) - await voting.setDelegate(delegate1, {from: holder20}) - - const delegate29 = await voting.getDelegate(holder29) - assert.equal(delegate29, delegate1, 'holder29 should have delegate1 as a delegate') - - const delegate20 = await voting.getDelegate(holder20) - assert.equal(delegate20, delegate1, 'holder20 should have delegate1 as a delegate') - - - const delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, defaultLimit))[0] - assertArraysEqualAsSets(delegatedVoters, [holder29, holder20], 'delegate1 should be a delegate of both holder29 and holder20') - }) - - it(`voter can't set the zero address as a delegate`, async () => { - await assertRevert( - voting.setDelegate(ZERO_ADDRESS, {from: holder29}), - ERRORS.VOTING_ZERO_ADDRESS_PASSED - ) - }) - - it(`voter can't assign themself as a delegate`, async () => { - await assertRevert( - voting.setDelegate(holder29, {from: holder29}), - ERRORS.VOTING_SELF_DELEGATE - ) - }) - - it(`voter can't assign their current delegate as a delegate`, async () => { - await voting.setDelegate(delegate1, {from: holder29}) - await assertRevert( - voting.setDelegate(delegate1, {from: holder29}), - ERRORS.VOTING_DELEGATE_SAME_AS_PREV - ) - }) - - it(`voter can unassign their delegate`, async () => { - await voting.setDelegate(delegate1, {from: holder29}) - - const tx = await voting.resetDelegate({from: holder29}) - assertEvent(tx, 'ResetDelegate', { - expectedArgs: {voter: holder29, delegate: delegate1} - }) - assertAmountOfEvents(tx, 'ResetDelegate', {expectedAmount: 1}) - - const delegate = await voting.getDelegate(holder29) - assert.equal(delegate, ZERO_ADDRESS, `holder29 shouldn't have a delegate anymore`) - - const delegatedVoters = (await voting.getDelegatedVoters(delegate2, 0, defaultLimit))[0] - assertArraysEqualAsSets(delegatedVoters, [], 'delegatedVoters should be empty') - }) - - it(`voter can't unassign their delegate if they wasn't assigned before`, async () => { - await assertRevert( - voting.resetDelegate({from: holder29}), - ERRORS.VOTING_DELEGATE_NOT_SET - ) - }) - - it(`voter can unassign a delegate who has multiple delegated voters`, async () => { - await voting.setDelegate(delegate1, {from: holder20}) - await voting.setDelegate(delegate1, {from: holder29}) - - await voting.resetDelegate({from: holder29}) - const delegate = await voting.getDelegate(holder29) - assert.equal(delegate, ZERO_ADDRESS, `holder29 shouldn't have a delegate anymore`) - - const delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, defaultLimit))[0] - assertArraysEqualAsSets(delegatedVoters, [holder20], 'delegatedVoters should contain only holder20') - }) - - it(`multiple voters can unassign the delegate`, async () => { - await voting.setDelegate(delegate1, {from: holder20}) - await voting.setDelegate(delegate1, {from: holder29}) - await voting.setDelegate(delegate1, {from: holder51}) - - await voting.resetDelegate({from: holder29}) - await voting.resetDelegate({from: holder51}) - - const delegate20 = await voting.getDelegate(holder20) - assert.equal(delegate20, delegate1, `holder20 should still have delegate1 as a delegate`) - - const delegate29 = await voting.getDelegate(holder29) - assert.equal(delegate29, ZERO_ADDRESS, `holder29 shouldn't have a delegate anymore`) - - const delegate51 = await voting.getDelegate(holder51) - assert.equal(delegate51, ZERO_ADDRESS, `holder51 shouldn't have a delegate anymore`) - - const delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, defaultLimit))[0] - assertArraysEqualAsSets(delegatedVoters, [holder20], 'delegatedVoters should contain only holder20') - }) - - }) - - context('delegated voters getters', () => { - const neededSupport = pct16(50) - const minimumAcceptanceQuorum = pct16(20) - const decimals = 18 - const defaultLimit = 100 - const voters = [{ - account: holder1, - balance: bigExp(1, decimals) - },{ - account: holder2, - balance: bigExp(2, decimals) - }, { - account: holder20, - balance: bigExp(20, decimals) - }, { - account: holder29, - balance: bigExp(29, decimals) - }] - - beforeEach(async () => { - token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', decimals, 'n', true) // empty parameters minime - - await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, 0) - - for (let i = 0; i < voters.length; i++) { - await token.generateTokens(voters[i].account, voters[i].balance) - await voting.setDelegate(delegate1, {from: voters[i].account}) - } - - executionTarget = await ExecutionTarget.new() - }) - - it('should return correct delegated voters count', async () => { - const delegatedVotersCount = (await voting.getDelegatedVotersCount(delegate1)).toNumber() - assert(delegatedVotersCount === voters.length) - }) - - it(`revert if "_delegate" is zero address`, async () => { - await assertRevert( - voting.getDelegatedVotersCount(ZERO_ADDRESS), - ERRORS.VOTING_ZERO_ADDRESS_PASSED - ) - - await assertRevert( - voting.getDelegatedVoters(ZERO_ADDRESS, 0, defaultLimit), - ERRORS.VOTING_ZERO_ADDRESS_PASSED - ) - }) - - it(`revert if "_limit" is 0`, async () => { - await assertRevert( - voting.getDelegatedVoters(ZERO_ADDRESS, 0, defaultLimit), - ERRORS.VOTING_ZERO_ADDRESS_PASSED - ) - }) - - it(`if delegatedVoters array length is 0, return two empty arrays`, async () => { - const delegatdVotersData = await voting.getDelegatedVoters(nonHolder, 0, defaultLimit) - assert(delegatdVotersData[0].length === 0, 'votersList should be empty') - assert(delegatdVotersData[1].length === 0, 'votingPowerList should be empty') - }) - - it(`should return correct delegated voters data if offset + limit >= votersCount`, async () => { - const offset = 2 - const limit = 5 - const delegatedVotersData = await voting.getDelegatedVoters(delegate1, offset, limit) - const delegatedVotersCount = (await voting.getDelegatedVotersCount(delegate1)).toNumber() - const delegatedVotersCountToReturn = delegatedVotersCount - offset - - assert(delegatedVotersData[0].length === delegatedVotersCountToReturn) - assert(delegatedVotersData[1].length === delegatedVotersCountToReturn) - - const votersSlice = voters.slice(offset, delegatedVotersCount) - const votersListSlice = votersSlice.map(voter => voter.account) - assertArraysEqualAsSets(delegatedVotersData[0], votersListSlice, 'votersList should be correct') - - const votingPowerListSlice = votersSlice.map((voter) => voter.balance.toString()) - const votingPowerList = delegatedVotersData[1].map(votingPower => votingPower.toString()) - assertArraysEqualAsSets(votingPowerList, votingPowerListSlice, 'votingPowerList should be correct') - }) - - it(`should return correct delegated voters data if offset + limit < votersCount`, async () => { - const offset = 1 - const limit = 1 - const delegatedVotersData = await voting.getDelegatedVoters(delegate1, offset, limit) - - assert(delegatedVotersData[0].length === limit) - assert(delegatedVotersData[1].length === limit) - - const votersSlice = voters.slice(offset, offset + limit) - const votersListSlice = votersSlice.map(voter => voter.account) - assertArraysEqualAsSets(delegatedVotersData[0], votersListSlice, 'votersList should be correct') - - const votingPowerListSlice = votersSlice.map((voter) => voter.balance.toString()) - const votingPowerList = delegatedVotersData[1].map(votingPower => votingPower.toString()) - assertArraysEqualAsSets(votingPowerList, votingPowerListSlice, 'votingPowerList should be correct') - }) - - it(`revert if _voter is zero address`, async () => { - await assertRevert( - voting.getDelegate(ZERO_ADDRESS), - ERRORS.VOTING_ZERO_ADDRESS_PASSED - ) - }) - - it(`return zero address if no delegate`, async () => { - const delegate = await voting.getDelegate(nonHolder) - assert.equal(delegate, ZERO_ADDRESS, 'should return zero address') - }) - - it(`can get voter's delegate address`, async () => { - const delegate = await voting.getDelegate(holder1) - assert.equal(delegate, delegate1, 'should return delegate1 address') - }) - }) - - context('voting as delegate', () => { - let script, voteId, creator, metadata - - const neededSupport = pct16(50) - const minimumAcceptanceQuorum = pct16(20) - const decimals = 18 - const voters = [{ - account: holder1, - balance: bigExp(1, decimals) - }, { - account: holder2, - balance: bigExp(2, decimals) - }, { - account: holder20, - balance: bigExp(20, decimals) - }, { - account: holder29, - balance: bigExp(29, decimals) - }, { - account: holder51, - balance: bigExp(51, decimals) - }] - - beforeEach(async () => { - token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', decimals, 'n', true) // empty parameters minime - - await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, objectionPhase) - - for (let i = 0; i < voters.length; i++) { - await token.generateTokens(voters[i].account, voters[i].balance) - await voting.setDelegate(delegate1, {from: voters[i].account}) - } - - executionTarget = await ExecutionTarget.new() - - const action = {to: executionTarget.address, calldata: executionTarget.contract.methods.execute().encodeABI()} - script = encodeCallScript([action, action]) - - await voting.resetDelegate({from: holder1}) - await token.transfer(holder51, bigExp(2, decimals), { from: holder2 }) - - const receipt = await voting.methods['newVote(bytes,string)'](script, 'metadata', {from: holder51}); - voteId = getEventArgument(receipt, 'StartVote', 'voteId') - creator = getEventArgument(receipt, 'StartVote', 'creator') - metadata = getEventArgument(receipt, 'StartVote', 'metadata') - }) - - it(`getVotersStateAtVote`, async () => { - await voting.vote(voteId, true, false, { from: holder20 }) - await voting.vote(voteId, false, false, { from: holder29 }) - await voting.attemptVoteForMultiple(voteId, false, [holder51], {from: delegate1}) - - const votersState = await voting.getVotersStateAtVote(voteId, [holder20, holder29, holder51]) - assert.equal(votersState[0], VOTER_STATE.YEA, `holder20 should have 'yea' state`) - assert.equal(votersState[1], VOTER_STATE.NAY, `holder29 should have 'nay' state`) - assert.equal(votersState[2], VOTER_STATE.DELEGATE_NAY, `holder51 should have 'delegateNay' state`) - }) - - it(`revert if vote does not exist`, async () => { - await assertRevert( - voting.attemptVoteForMultiple(voteId + 1, false, [holder51], {from: delegate1}), - ERRORS.VOTING_NO_VOTE - ) - }) - - it(`revert if vote has already ended`, async () => { - await voting.mockIncreaseTime(votingDuration + 1) - await assertRevert( - voting.attemptVoteForMultiple(voteId, false, [holder51], {from: delegate1}), - ERRORS.VOTING_CAN_NOT_VOTE - ) - }) - - it(`revert if vote has already been executed`, async () => { - await voting.vote(voteId, true, true, { from: holder51 }) - await voting.mockIncreaseTime(votingDuration + 1) - await voting.executeVote(voteId) - - await assertRevert( - voting.attemptVoteForMultiple(voteId, true, [holder51], {from: delegate1}), - ERRORS.VOTING_CAN_NOT_VOTE - ) - }) - - it(`revert if trying to vote 'yea' during objection phase`, async () => { - await voting.mockIncreaseTime(mainPhase + 200) - await assertRevert( - voting.attemptVoteForMultiple(voteId, true, [holder51], {from: delegate1}), - ERRORS.VOTING_CAN_NOT_VOTE - ) - }) - - it(`revert if one of the voters has 0 LDO`, async () => { - // holder 2 has 0 LDO - await assertRevert( - voting.attemptVoteForMultiple(voteId, true, [holder51, holder2, holder1], {from: delegate1}), - ERRORS.VOTING_NO_VOTING_POWER - ) - }) - - it(`one of the voters voted beforehand`, async () => { - await voting.vote(voteId, false, false, { from: holder29 }) - const tx = await voting.attemptVoteForMultiple(voteId, true, [holder20, holder29], {from: delegate1}); - - assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) - assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 1}) - assertEvent(tx, 'CastVote', {expectedArgs: {voteId, voter: holder20, supports: true}}) - assertEvent(tx, 'CastVoteAsDelegate', {expectedArgs: {voteId, delegate: delegate1, voter: holder20, supports: true}}) - - assert.equal(await voting.getVoterState(voteId, holder29), VOTER_STATE.NAY, `holder29 should have 'nay' state`) - assert.equal(await voting.getVoterState(voteId, holder20), VOTER_STATE.DELEGATE_YEA, `holder20 should have 'delegateYea' state`) - }) - - it(`vote for multiple with duplicates`, async () => { - const tx = await voting.attemptVoteForMultiple(voteId, false, [holder29, holder29], {from: delegate1}); - - assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 2}) - assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 2}) - assert.equal(await voting.getVoterState(voteId, holder29), VOTER_STATE.DELEGATE_NAY, `holder29 should have 'delegateNay' state`) - const vote = await voting.getVote(voteId) - assertBn(vote.nay, bigExp(29, decimals), 'nay should be 29') - }) - - it(`vote for empty list`, async () => { - await assertRevert( - voting.attemptVoteForMultiple(voteId, false, [], {from: delegate1}), - ERRORS.VOTING_CAN_NOT_VOTE_FOR - ) - }) - - it(`skipped vote for multiple for all voters from list`, async () => { - await voting.vote(voteId, false, false, { from: holder20 }) - await voting.vote(voteId, false, false, { from: holder29 }) - await assertRevert( - voting.attemptVoteForMultiple(voteId, false, [holder20, holder29], {from: delegate1}), - ERRORS.VOTING_CAN_NOT_VOTE_FOR - ) - }) - - it(`successful vote for multiple`, async () => { - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 100, voteId) - const delegatedVotersAddresses = delegatedVotersData[0] - const delegatedVotersVotingPower = delegatedVotersData[1] - const filteredDelegatedVoters = [] - for (let i = 0; i < delegatedVotersAddresses.length; i++) { - const votingPower = delegatedVotersVotingPower[i] - if (votingPower.gt(bigExp(0, decimals))) { - filteredDelegatedVoters.push({address: delegatedVotersAddresses[i], votingPower}) - } - } - const filteredDelegatedVotersAddresses = filteredDelegatedVoters.map(({address}) => address) - const tx = await voting.attemptVoteForMultiple( - voteId, - false, - filteredDelegatedVotersAddresses, - {from: delegate1} - ); - - // Check amount of events - assertAmountOfEvents(tx, 'CastVote', {expectedAmount: filteredDelegatedVoters.length}) - assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: filteredDelegatedVoters.length}) - - // Check events content - for (let i = 0; i < filteredDelegatedVoters.length; i++) { - const {address, votingPower} = filteredDelegatedVoters[i] - assertEvent(tx, 'CastVote', {index: i, expectedArgs: {voteId, voter: address, supports: false, stake: votingPower}}) - assertEvent(tx, 'CastVoteAsDelegate', {index: i, expectedArgs: {voteId, delegate: delegate1, voter: address, supports: false, stake: votingPower}}) - } - - // Check voters' state - const votersState = await voting.getVotersStateAtVote(voteId, filteredDelegatedVotersAddresses) - votersState.every((state) => { - assert.equal(state, VOTER_STATE.DELEGATE_NAY.toString(), `voter should have 'delegateNay' state`) - }) - - // Check applied VP - const vote = await voting.getVote(voteId) - const votingPowerSum = filteredDelegatedVoters.reduce( - (sum, {votingPower}) => sum.add(votingPower), - bigExp(0, decimals) - ) - - assertBn(vote.yea, bigExp(0, decimals), 'yea should be 0') - assertBn(vote.nay, votingPowerSum, 'nay should be sum of all VP') - }) - - it(`successful vote for single`, async () => { - const tx = await voting.attemptVoteFor(voteId, false, holder29, {from: delegate1}) - - // Check amount of events - assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) - assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 1}) - - const holder29VP = bigExp(29, decimals) - - // Check events content - assertEvent(tx, 'CastVote', { expectedArgs: {voteId, voter: holder29, supports: false, stake: holder29VP}}) - assertEvent(tx, 'CastVoteAsDelegate', { expectedArgs: {voteId, delegate: delegate1, voter: holder29, supports: false, stake: holder29VP}}) - - - // Check voter's state - assert.equal(await voting.getVoterState(voteId, holder29), VOTER_STATE.DELEGATE_NAY, `holder29 should have 'delegateNay' state`) - - // Check applied VP - const vote = await voting.getVote(voteId) - assertBn(vote.yea, bigExp(0, decimals), 'yea should be 0') - assertBn(vote.nay, holder29VP, 'nay should be holder29 VP') - }) - - }) - }) From 94e327614f7ec8785b6fe9cf4805150877a679d6 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Thu, 18 Apr 2024 12:16:46 +0200 Subject: [PATCH 049/100] refactor: rename setDelegate to assign delegate; rename resetDelegate to unassignDelegate; rename corresponding events --- apps/voting/contracts/Voting.sol | 12 +-- apps/voting/test/delegation.js | 138 +++++++++++++++---------------- 2 files changed, 75 insertions(+), 75 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 817558876..efc8337f3 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -96,8 +96,8 @@ contract Voting is IForwarder, AragonApp { event ChangeMinQuorum(uint64 minAcceptQuorumPct); event ChangeVoteTime(uint64 voteTime); event ChangeObjectionPhaseTime(uint64 objectionPhaseTime); - event SetDelegate(address indexed voter, address indexed delegate); - event ResetDelegate(address indexed voter, address indexed delegate); + event AssignDelegate(address indexed voter, address indexed delegate); + event UnassignDelegate(address indexed voter, address indexed unassignedDelegate); event CastVoteAsDelegate(uint256 indexed voteId, address indexed delegate, address indexed voter, bool supports, uint256 stake); modifier voteExists(uint256 _voteId) { @@ -244,7 +244,7 @@ contract Voting is IForwarder, AragonApp { * @notice Assign `_delegate` as the delegate for the sender * @param _delegate address to delegate to */ - function setDelegate(address _delegate) external { + function assignDelegate(address _delegate) external { require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); require(_delegate != msg.sender, ERROR_SELF_DELEGATE); @@ -260,7 +260,7 @@ contract Voting is IForwarder, AragonApp { /** * @notice Unassign `_delegate` from the sender */ - function resetDelegate() external { + function unassignDelegate() external { address prevDelegate = delegates[msg.sender].delegate; require(prevDelegate != address(0), ERROR_DELEGATE_NOT_SET); @@ -591,7 +591,7 @@ contract Voting is IForwarder, AragonApp { delegatedVoters[_delegate].addresses.push(_voter); delegates[_voter] = Delegate(_delegate, uint96(delegatedVotersCount)); - emit SetDelegate(_voter, _delegate); + emit AssignDelegate(_voter, _delegate); } /** @@ -611,7 +611,7 @@ contract Voting is IForwarder, AragonApp { delegates[lastVoter].voterIndex = voterIndex; } delegatedVoters[_delegate].addresses.length--; - emit ResetDelegate(_voter, _delegate); + emit UnassignDelegate(_voter, _delegate); } /** diff --git a/apps/voting/test/delegation.js b/apps/voting/test/delegation.js index 6922433de..bfe0e9928 100644 --- a/apps/voting/test/delegation.js +++ b/apps/voting/test/delegation.js @@ -67,12 +67,12 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 [holder51]: LDO51, } - const setDelegate = async (delegate, holder ) => { - const tx = await voting.setDelegate(delegate, {from: holder}) - assertEvent(tx, 'SetDelegate', { + const assignDelegate = async (delegate, holder ) => { + const tx = await voting.assignDelegate(delegate, {from: holder}) + assertEvent(tx, 'AssignDelegate', { expectedArgs: {voter: holder, delegate} }) - assertAmountOfEvents(tx, 'SetDelegate', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'AssignDelegate', {expectedAmount: 1}) } const attemptVoteFor = async (voteId, supports, holder, delegate) => { const tx = await voting.attemptVoteFor(voteId, supports, holder, {from: delegate}) @@ -136,29 +136,29 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 it(`voter can't set the zero address as a delegate`, async () => { await assertRevert( - voting.setDelegate(ZERO_ADDRESS, {from: holder29}), + voting.assignDelegate(ZERO_ADDRESS, {from: holder29}), ERRORS.VOTING_ZERO_ADDRESS_PASSED ) }) it(`voter can't assign themself as a delegate`, async () => { await assertRevert( - voting.setDelegate(holder29, {from: holder29}), + voting.assignDelegate(holder29, {from: holder29}), ERRORS.VOTING_SELF_DELEGATE ) }) it(`voter can't assign their current delegate as a delegate`, async () => { - await voting.setDelegate(delegate1, {from: holder29}) + await voting.assignDelegate(delegate1, {from: holder29}) await assertRevert( - voting.setDelegate(delegate1, {from: holder29}), + voting.assignDelegate(delegate1, {from: holder29}), ERRORS.VOTING_DELEGATE_SAME_AS_PREV ) }) it(`voter can't unassign their delegate if they wasn't assigned before`, async () => { await assertRevert( - voting.resetDelegate({from: holder29}), + voting.unassignDelegate({from: holder29}), ERRORS.VOTING_DELEGATE_NOT_SET ) }) @@ -167,11 +167,11 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 let delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] assert.equal(delegatedVoters.length, 0, 'delegate1 should not be a delegate of anyone') - const tx = await voting.setDelegate(delegate1, {from: holder29}) - assertEvent(tx, 'SetDelegate', { + const tx = await voting.assignDelegate(delegate1, {from: holder29}) + assertEvent(tx, 'AssignDelegate', { expectedArgs: {voter: holder29, delegate: delegate1} }) - assertAmountOfEvents(tx, 'SetDelegate', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'AssignDelegate', {expectedAmount: 1}) const delegate = await voting.getDelegate(holder29) assert.equal(delegate, delegate1, 'holder29 should have delegate1 as a delegate') @@ -184,16 +184,16 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 let delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] assert.equal(delegatedVoters.length, 0, 'delegate1 should not be a delegate of anyone') - await voting.setDelegate(delegate1, {from: holder29}) + await voting.assignDelegate(delegate1, {from: holder29}) delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] assertArraysEqualAsSets(delegatedVoters, [holder29], 'delegate1 should be a delegate of holder29') - const tx = await voting.resetDelegate({from: holder29}) - assertEvent(tx, 'ResetDelegate', { - expectedArgs: {voter: holder29, delegate: delegate1} + const tx = await voting.unassignDelegate({from: holder29}) + assertEvent(tx, 'UnassignDelegate', { + expectedArgs: {voter: holder29, unassignedDelegate: delegate1} }) - assertAmountOfEvents(tx, 'ResetDelegate', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'UnassignDelegate', {expectedAmount: 1}) delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] assertArraysEqualAsSets(delegatedVoters, [], 'delegate1 should not be a delegate of anyone') }) @@ -202,23 +202,23 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 let delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] assert.equal(delegatedVoters.length, 0, 'delegate1 should not be a delegate of anyone') - await voting.setDelegate(delegate1, {from: holder20}) - await voting.setDelegate(delegate1, {from: holder29}) - await voting.setDelegate(delegate1, {from: holder51}) + await voting.assignDelegate(delegate1, {from: holder20}) + await voting.assignDelegate(delegate1, {from: holder29}) + await voting.assignDelegate(delegate1, {from: holder51}) - const tx1 = await voting.resetDelegate({from: holder29}) - assertEvent(tx1, 'ResetDelegate', { - expectedArgs: {voter: holder29, delegate: delegate1} + const tx1 = await voting.unassignDelegate({from: holder29}) + assertEvent(tx1, 'UnassignDelegate', { + expectedArgs: {voter: holder29, unassignedDelegate: delegate1} }) - assertAmountOfEvents(tx1, 'ResetDelegate', {expectedAmount: 1}) + assertAmountOfEvents(tx1, 'UnassignDelegate', {expectedAmount: 1}) delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 5))[0] assertArraysEqualAsSets(delegatedVoters, [holder20, holder51], 'delegate1 have holder20 and holder51 as a delegated voters') - const tx2 = await voting.resetDelegate({from: holder51}) - assertEvent(tx2, 'ResetDelegate', { - expectedArgs: {voter: holder51, delegate: delegate1} + const tx2 = await voting.unassignDelegate({from: holder51}) + assertEvent(tx2, 'UnassignDelegate', { + expectedArgs: {voter: holder51, unassignedDelegate: delegate1} }) - assertAmountOfEvents(tx2, 'ResetDelegate', {expectedAmount: 1}) + assertAmountOfEvents(tx2, 'UnassignDelegate', {expectedAmount: 1}) delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] assertArraysEqualAsSets(delegatedVoters, [holder20], 'delegate1 have only holder20 as a delegated voter') }) @@ -229,10 +229,10 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 delegatedVoters = (await voting.getDelegatedVoters(delegate2, 0, 1))[0] assert.equal(delegatedVoters.length, 0, 'delegate2 should not be a delegate of anyone') - await voting.setDelegate(delegate1, {from: holder29}) - await voting.setDelegate(delegate2, {from: holder51}) + await voting.assignDelegate(delegate1, {from: holder29}) + await voting.assignDelegate(delegate2, {from: holder51}) - await voting.setDelegate(delegate2, {from: holder29}) + await voting.assignDelegate(delegate2, {from: holder29}) const delegatedVotersDelegate1 = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] assertArraysEqualAsSets(delegatedVotersDelegate1, [], 'delegate1 should not be a delegate of anyone') @@ -244,17 +244,17 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 let delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] assert.equal(delegatedVoters.length, 0, 'delegate1 should not be a delegate of anyone') - const tx1 = await voting.setDelegate(delegate1, {from: holder29}) - assertEvent(tx1, 'SetDelegate', { + const tx1 = await voting.assignDelegate(delegate1, {from: holder29}) + assertEvent(tx1, 'AssignDelegate', { expectedArgs: {voter: holder29, delegate: delegate1} }) - assertAmountOfEvents(tx1, 'SetDelegate', {expectedAmount: 1}) + assertAmountOfEvents(tx1, 'AssignDelegate', {expectedAmount: 1}) - const tx2 = await voting.setDelegate(delegate1, {from: holder51}) - assertEvent(tx2, 'SetDelegate', { + const tx2 = await voting.assignDelegate(delegate1, {from: holder51}) + assertEvent(tx2, 'AssignDelegate', { expectedArgs: {voter: holder51, delegate: delegate1} }) - assertAmountOfEvents(tx2, 'SetDelegate', {expectedAmount: 1}) + assertAmountOfEvents(tx2, 'AssignDelegate', {expectedAmount: 1}) delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 2))[0] assertArraysEqualAsSets(delegatedVoters, [holder29, holder51], 'delegate1 should be a delegate of holder29 and holder51') @@ -268,7 +268,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const delegateList= [ [delegate1, holder1], [delegate1, holder20] ] for (const [delegate, holder] of delegateList) { - await setDelegate(delegate, holder) + await assignDelegate(delegate, holder) } const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) @@ -292,7 +292,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 it('delegate can manage several voters and vote all (voteForMulti)', async () => { const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] for (const [delegate, holder] of delegateList) { - await setDelegate(delegate, holder) + await assignDelegate(delegate, holder) } const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) @@ -314,7 +314,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const delegateList= [ [delegate1, holder1], [delegate1, holder20] ] for (const [delegate, holder] of delegateList) { - await setDelegate(delegate, holder) + await assignDelegate(delegate, holder) } const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) @@ -338,7 +338,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 it('delegate can manage several voters and vote for first (voteForMulti)', async () => { const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] for (const [delegate, holder] of delegateList) { - setDelegate(delegate, holder) + assignDelegate(delegate, holder) } const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) @@ -362,7 +362,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 it('delegated voter can overwrite a delegates vote (voteFor)', async () => { const [ delegate, holder] = [ delegate1, holder1 ] - setDelegate(delegate, holder) + assignDelegate(delegate, holder) const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) @@ -387,7 +387,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 it('delegated voter can overwrite a delegates vote (voteForMulti)', async () => { const [ delegate, holder] = [ delegate2, holder29 ] - await setDelegate(delegate, holder) + await assignDelegate(delegate, holder) const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) @@ -412,13 +412,13 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 it('delegate can vote for a voter that delegated them their voting power during the active phase (voteFor)', async () => { const [ delegate, holder] = [ delegate2, holder1 ] - await setDelegate(delegate, holder) + await assignDelegate(delegate, holder) const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) const { phase } = await voting.getVote(voteId) assert.equal(phase, MAIN_PHASE) - await setDelegate(delegate1, holder) + await assignDelegate(delegate1, holder) const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) assertArraysEqualAsSets(delegatedVotersData[0], [ holder1 ]) @@ -435,13 +435,13 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 it('delegate can vote for a voter that delegated them their voting power during the active phase (voteForMulti)', async () => { const [ delegate, holder] = [ delegate1, holder29 ] - await setDelegate(delegate, holder) + await assignDelegate(delegate, holder) const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) const { phase } = await voting.getVote(voteId) assert.equal(phase, MAIN_PHASE) - await setDelegate(delegate2, holder) + await assignDelegate(delegate2, holder) const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) assertArraysEqualAsSets(delegatedVotersData[0], [holder29]) @@ -457,13 +457,13 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 it('delegate cant vote for a voter that acquired voting power during the active phase of the vote (voteFor)', async () => { const [ delegate, holder] = [ delegate1, holder1 ] - await setDelegate(delegate, holder) + await assignDelegate(delegate, holder) const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) const { phase } = await voting.getVote(voteId) assert.equal(phase, MAIN_PHASE) - await setDelegate(delegate2, holder) + await assignDelegate(delegate2, holder) const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) assertArraysEqualAsSets(delegatedVotersData[0], [ ]) @@ -474,13 +474,13 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 it('delegate cant vote for a voter that acquired voting power during the active phase of the vote (voteForMulti)', async () => { const [ delegate, holder] = [ delegate2, holder29 ] - await setDelegate(delegate, holder) + await assignDelegate(delegate, holder) const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) const { phase } = await voting.getVote(voteId) assert.equal(phase, MAIN_PHASE) - await setDelegate(delegate1, holder) + await assignDelegate(delegate1, holder) const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) assertArraysEqualAsSets(delegatedVotersData[0], []) @@ -494,7 +494,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const delegateList= [ [delegate1, holder1], [delegate1, holder20] ] for (const [delegate, holder] of delegateList) { - await setDelegate(delegate, holder) + await assignDelegate(delegate, holder) } const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) @@ -519,7 +519,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 it('delegate vote by snapshot vp not current (voteForMulti)', async () => { const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] for (const [delegate, holder] of delegateList) { - await setDelegate(delegate, holder) + await assignDelegate(delegate, holder) } const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) @@ -543,7 +543,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const delegateList = [[delegate1, holder1], [delegate1, holder20]] for (const [delegate, holder] of delegateList) { - await setDelegate(delegate, holder) + await assignDelegate(delegate, holder) } const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) @@ -571,7 +571,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 it('delegate change mind (voteForMulti)', async () => { const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] for (const [delegate, holder] of delegateList) { - await setDelegate(delegate, holder) + await assignDelegate(delegate, holder) } const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) @@ -596,7 +596,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const delegateList = [[delegate1, holder1], [delegate1, holder20]] for (const [delegate, holder] of delegateList) { - await setDelegate(delegate, holder) + await assignDelegate(delegate, holder) } const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) @@ -628,7 +628,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 it('delegate vote "yes" in main phase, delegate vote "no" in objection (voteForMulti)', async () => { const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] for (const [delegate, holder] of delegateList) { - await setDelegate(delegate, holder) + await assignDelegate(delegate, holder) } const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) @@ -658,7 +658,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const delegateList = [[delegate1, holder1], [delegate1, holder20]] for (const [delegate, holder] of delegateList) { - await setDelegate(delegate, holder) + await assignDelegate(delegate, holder) } const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) @@ -679,7 +679,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const objectorList = [[delegate2, holder1], [delegate2, holder20]] for (const [delegate, holder] of objectorList) { - await setDelegate(delegate, holder) + await assignDelegate(delegate, holder) } for (const [ delegate , holder] of objectorList) { @@ -696,7 +696,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 it('delegate vote "yes" in main phase, new delegate vote "no" in objection (voteForMulti)', async () => { const delegateList= [[delegate2, holder29], [delegate2, holder51]] for (const [delegate, holder] of delegateList) { - await setDelegate(delegate, holder) + await assignDelegate(delegate, holder) } const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) @@ -715,7 +715,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const objectorList = [[delegate1, holder29], [delegate1, holder51]] for (const [delegate, holder] of objectorList) { - await setDelegate(delegate, holder) + await assignDelegate(delegate, holder) } await attemptVoteForMultiple(voteId, false, delegatedVotersData[0], delegate1) @@ -732,7 +732,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const delegateList = [[delegate1, holder1], [delegate1, holder20]] for (const [delegate, holder] of delegateList) { - await setDelegate(delegate, holder) + await assignDelegate(delegate, holder) } const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) @@ -764,7 +764,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 it('delegate vote in main phase, voter overpower in objection (voteForMulti)', async () => { const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] for (const [delegate, holder] of delegateList) { - await setDelegate(delegate, holder) + await assignDelegate(delegate, holder) } const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) @@ -792,12 +792,12 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 // If a delegate was spammed by a large amount of fake delegated voters, they can still easily // retrieve an actual voters list and vote for that list. it('delegate can vote after spam', async () => { - await setDelegate(delegate2, holder29) + await assignDelegate(delegate2, holder29) for (const holder of spamHolders) { await token.generateTokens(holder, LDO3) - await setDelegate(delegate2, holder) + await assignDelegate(delegate2, holder) } - await setDelegate(delegate2, holder51) + await assignDelegate(delegate2, holder51) const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) @@ -844,7 +844,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 for (let i = 0; i < voters.length; i++) { await token.generateTokens(voters[i].account, voters[i].balance) - await voting.setDelegate(delegate1, {from: voters[i].account}) + await voting.assignDelegate(delegate1, {from: voters[i].account}) } executionTarget = await ExecutionTarget.new() @@ -964,7 +964,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 for (let i = 0; i < voters.length; i++) { await token.generateTokens(voters[i].account, voters[i].balance) - await voting.setDelegate(delegate1, {from: voters[i].account}) + await voting.assignDelegate(delegate1, {from: voters[i].account}) } executionTarget = await ExecutionTarget.new() @@ -972,7 +972,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const action = {to: executionTarget.address, calldata: executionTarget.contract.methods.execute().encodeABI()} script = encodeCallScript([action, action]) - await voting.resetDelegate({from: holder1}) + await voting.unassignDelegate({from: holder1}) await token.transfer(holder51, bigExp(2, decimals), { from: holder2 }) const receipt = await voting.methods['newVote(bytes,string)'](script, 'metadata', {from: holder51}); From 5ef1358d79f3f47f136067ccf41c4bef9484179e Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Thu, 18 Apr 2024 15:07:19 +0200 Subject: [PATCH 050/100] refactor: UINT_96_MAX value --- apps/voting/contracts/Voting.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index efc8337f3..bb01b3225 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -24,7 +24,7 @@ contract Voting is IForwarder, AragonApp { uint64 public constant PCT_BASE = 10 ** 18; // 0% = 0; 1% = 10^16; 100% = 10^18 - uint256 private constant UINT_96_MAX = 0xFFFFFFFFFFFFFFFFFFFFFFFF; + uint256 private constant UINT_96_MAX = 2 ** 96 - 1; string private constant ERROR_NO_VOTE = "VOTING_NO_VOTE"; string private constant ERROR_INIT_PCTS = "VOTING_INIT_PCTS"; string private constant ERROR_CHANGE_SUPPORT_PCTS = "VOTING_CHANGE_SUPPORT_PCTS"; From c4d9e99aa241c6aa421cf5fb0aaf999a3a6f0634 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Thu, 18 Apr 2024 15:08:56 +0200 Subject: [PATCH 051/100] chore: fix comments copy --- apps/voting/contracts/Voting.sol | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index bb01b3225..83abacf32 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -307,7 +307,7 @@ contract Voting is IForwarder, AragonApp { // Forwarding fns /** - * @notice Tells whether the Voting app is a forwarder or not + * @notice Tells whether the Voting app is a forwarder * @dev IForwarder interface conformance * @return Always true */ @@ -326,7 +326,7 @@ contract Voting is IForwarder, AragonApp { } /** - * @notice Tells whether `_sender` can forward actions or not + * @notice Tells whether `_sender` can forward actions * @dev IForwarder interface conformance * @param _sender Address of the account intending to forward an action * @return True if the given address can create votes, false otherwise @@ -339,7 +339,7 @@ contract Voting is IForwarder, AragonApp { // Getter fns /** - * @notice Tells whether a vote #`_voteId` can be executed or not + * @notice Tells whether a vote #`_voteId` can be executed * @dev Initialization check is implicitly provided by `voteExists()` as new votes can only be * created via `newVote(),` which requires initialization * @param _voteId Vote identifier @@ -519,9 +519,9 @@ contract Voting is IForwarder, AragonApp { * @dev Internal function to cast a vote or object to. * @dev It assumes that voter can support or object to the vote * @param _voteId The identifier of the vote - * @param _supports Whether the voter supports the vote or not + * @param _supports Whether the voter supports the vote * @param _voter The address of the voter - * @param _isDelegate Whether the voter is a delegate or not + * @param _isDelegate Whether the voter is a delegate */ function _vote(uint256 _voteId, bool _supports, address _voter, bool _isDelegate) internal { Vote storage vote_ = votes[_voteId]; @@ -679,7 +679,7 @@ contract Voting is IForwarder, AragonApp { * @dev Internal function to check if the vote is open and given option is applicable at the current phase. * It assumes the queried vote exists. * @param vote_ The queried vote - * @param _supports Whether the voter supports the vote or not + * @param _supports Whether the voter supports the vote * @return True if the given voter can participate a certain vote, false otherwise */ function _isValidPhaseToVote(Vote storage vote_, bool _supports) internal view returns (bool) { From cda793054d047934938d039402b76dc4696b1fa7 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Thu, 18 Apr 2024 16:46:39 +0200 Subject: [PATCH 052/100] bump hardhat version --- apps/voting/hardhat.config.js | 2 +- apps/voting/package.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/voting/hardhat.config.js b/apps/voting/hardhat.config.js index 27578e3b7..954ef96da 100644 --- a/apps/voting/hardhat.config.js +++ b/apps/voting/hardhat.config.js @@ -40,8 +40,8 @@ const getNetConfig = (networkName, ethAccountName) => { dev, hardhat: { blockGasLimit: 20000000, + hardfork: 'cancun', accounts: { - mnemonic: 'hardhat', count: 500, accountsBalance: '100000000000000000000000', gasPrice: 0 diff --git a/apps/voting/package.json b/apps/voting/package.json index 78075dca5..aff0e2c38 100644 --- a/apps/voting/package.json +++ b/apps/voting/package.json @@ -43,10 +43,10 @@ "@nomiclabs/hardhat-ethers": "^2.0.2", "@nomiclabs/hardhat-etherscan": "^3.1.8", "hardhat-abi-exporter": "^2.8.0", - "hardhat-gas-reporter": "^1.0.8", + "hardhat-gas-reporter": "^2.1.1", "chai": "^4.2.0", "ethers": "^5.4.7", - "hardhat": "^2.6.5", + "hardhat": "^2.22.2", "solidity-coverage": "^0.7.9", "solium": "^1.2.5", "web3": "^1.3.0" From e826ff074619259e3538cd83668713b1b3da2d54 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Thu, 18 Apr 2024 17:02:11 +0200 Subject: [PATCH 053/100] refactor: move removal from delegates map to _removeDelegatedAddressFor --- apps/voting/contracts/Voting.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 83abacf32..271ba898f 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -265,7 +265,6 @@ contract Voting is IForwarder, AragonApp { require(prevDelegate != address(0), ERROR_DELEGATE_NOT_SET); _removeDelegatedAddressFor(prevDelegate, msg.sender); - delete delegates[msg.sender]; } /** @@ -611,6 +610,7 @@ contract Voting is IForwarder, AragonApp { delegates[lastVoter].voterIndex = voterIndex; } delegatedVoters[_delegate].addresses.length--; + delete delegates[_voter]; emit UnassignDelegate(_voter, _delegate); } From 5edff759f36cded7794c379a3299dfcd57844a59 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Thu, 18 Apr 2024 17:10:04 +0200 Subject: [PATCH 054/100] refactor: optimize delegate removal --- apps/voting/contracts/Voting.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 271ba898f..4d47bd77a 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -604,8 +604,8 @@ contract Voting is IForwarder, AragonApp { uint96 voterIndex = delegates[_voter].voterIndex; assert(delegatedVoters[_delegate].addresses[voterIndex] == _voter); - address lastVoter = delegatedVoters[_delegate].addresses[delegatedVotersCount - 1]; if (voterIndex < delegatedVotersCount - 1) { + address lastVoter = delegatedVoters[_delegate].addresses[delegatedVotersCount - 1]; delegatedVoters[_delegate].addresses[voterIndex] = lastVoter; delegates[lastVoter].voterIndex = voterIndex; } From c035a74ea190743c71482290880cf01099d4f0ba Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Thu, 18 Apr 2024 17:12:01 +0200 Subject: [PATCH 055/100] refactor: rename hasManagedToVote to votedForAtLeastOne --- apps/voting/contracts/Voting.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 4d47bd77a..c5c0c24ef 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -276,7 +276,7 @@ contract Voting is IForwarder, AragonApp { function attemptVoteForMultiple(uint256 _voteId, bool _supports, address[] _voters) public voteExists(_voteId) { Vote storage vote_ = votes[_voteId]; require(_isValidPhaseToVote(vote_, _supports), ERROR_CAN_NOT_VOTE); - bool hasManagedToVote = false; + bool votedForAtLeastOne = false; address voter; for (uint256 i = 0; i < _voters.length; ++i) { @@ -284,11 +284,11 @@ contract Voting is IForwarder, AragonApp { require(_hasVotingPower(vote_, voter), ERROR_NO_VOTING_POWER); if (_canVoteFor(vote_, msg.sender, voter)) { _vote(_voteId, _supports, voter, true); - hasManagedToVote = true; + votedForAtLeastOne = true; } } - require(hasManagedToVote, ERROR_CAN_NOT_VOTE_FOR); + require(votedForAtLeastOne, ERROR_CAN_NOT_VOTE_FOR); } /** From 6c2840a9caea528321b3fbfbc0820c998c9aa37d Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Thu, 18 Apr 2024 17:14:24 +0200 Subject: [PATCH 056/100] fix: remove voteExists from attemptVoteFor --- apps/voting/contracts/Voting.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index c5c0c24ef..ac4b0a763 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -297,7 +297,7 @@ contract Voting is IForwarder, AragonApp { * @param _supports Whether the delegate supports the vote * @param _voter address of the voter */ - function attemptVoteFor(uint256 _voteId, bool _supports, address _voter) external voteExists(_voteId) { + function attemptVoteFor(uint256 _voteId, bool _supports, address _voter) external { address[] memory voters = new address[](1); voters[0] = _voter; attemptVoteForMultiple(_voteId, _supports, voters); From 8ec459c5b194b304aabf010a046c97f8eae259b6 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Thu, 18 Apr 2024 18:24:16 +0200 Subject: [PATCH 057/100] refactor: otimize gas usage of _removeDelegatedAddressFor, remove excess check --- apps/voting/contracts/Voting.sol | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index ac4b0a763..cb8f773ea 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -599,17 +599,17 @@ contract Voting is IForwarder, AragonApp { * @param _voter address of the voter */ function _removeDelegatedAddressFor(address _delegate, address _voter) internal { - uint256 delegatedVotersCount = delegatedVoters[_delegate].addresses.length; - require(delegatedVotersCount > 0, ERROR_DELEGATE_NOT_SET); - uint96 voterIndex = delegates[_voter].voterIndex; - assert(delegatedVoters[_delegate].addresses[voterIndex] == _voter); - if (voterIndex < delegatedVotersCount - 1) { - address lastVoter = delegatedVoters[_delegate].addresses[delegatedVotersCount - 1]; - delegatedVoters[_delegate].addresses[voterIndex] = lastVoter; + address[] storage votersList = delegatedVoters[_delegate].addresses; + assert(votersList[voterIndex] == _voter); + + uint256 lastVoterIndex = votersList.length - 1; + if (voterIndex < lastVoterIndex) { + address lastVoter = votersList[lastVoterIndex]; + votersList[voterIndex] = lastVoter; delegates[lastVoter].voterIndex = voterIndex; } - delegatedVoters[_delegate].addresses.length--; + votersList.length--; delete delegates[_voter]; emit UnassignDelegate(_voter, _delegate); } From d33e9aa9014a39eb3623634bc95b83c82fa75280 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Mon, 22 Apr 2024 12:50:16 +0200 Subject: [PATCH 058/100] refactor: remove _isDelegateFor and _hasVotedDirectly, add their logic into _canVoteFor --- apps/voting/contracts/Voting.sol | 38 ++++++++++---------------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index cb8f773ea..7219b52d7 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -282,7 +282,7 @@ contract Voting is IForwarder, AragonApp { for (uint256 i = 0; i < _voters.length; ++i) { voter = _voters[i]; require(_hasVotingPower(vote_, voter), ERROR_NO_VOTING_POWER); - if (_canVoteFor(vote_, msg.sender, voter)) { + if (_canVoteFor(vote_, voter, msg.sender)) { _vote(_voteId, _supports, voter, true); votedForAtLeastOne = true; } @@ -686,19 +686,6 @@ contract Voting is IForwarder, AragonApp { return _isVoteOpen(vote_) && (!_supports || _getVotePhase(vote_) == VotePhase.Main); } - /** - * @dev Internal function to check if the _delegate is a current delegate for the _voter - * @param _delegate address of the delegate - * @param _voter address of the voter - * @return True if _delegate is a current delegate for the _voter, false otherwise - */ - function _isDelegateFor(address _delegate, address _voter) internal view returns (bool) { - if (_delegate == address(0) || _voter == address(0)) { - return false; - } - return delegates[_voter].delegate == _delegate; - } - /** * @dev Internal function to check if the _delegate can vote on behalf of the _voter in the given vote * @param vote_ The queried vote @@ -706,19 +693,18 @@ contract Voting is IForwarder, AragonApp { * @param _voter address of the voter * @return True if the _delegate can vote on behalf of the _voter in the given vote, false otherwise */ - function _canVoteFor(Vote storage vote_, address _delegate, address _voter) internal view returns (bool) { - return _isDelegateFor(_delegate, _voter) && !_hasVotedDirectly(vote_, _voter); - } - - /** - * @dev Internal function to check if the _voter has voted by themselves in the given vote - * @param vote_ The queried vote - * @param _voter address of the voter - * @return True if the _voter has voted by themselves in the given vote, false otherwise - */ - function _hasVotedDirectly(Vote storage vote_, address _voter) internal view returns (bool) { + function _canVoteFor(Vote storage vote_, address _voter, address _delegate) internal view returns (bool) { + // Zero addresses are not allowed + if (_delegate == address(0) || _voter == address(0)) { + return false; + } + // The _delegate must be a delegate for the _voter + if (delegates[_voter].delegate != _delegate) { + return false; + } + // Otherwise, the _voter must not have voted directly VoterState state = vote_.voters[_voter]; - return state == VoterState.Yea || state == VoterState.Nay; + return state != VoterState.Yea && state != VoterState.Nay; } /** From cd8697b8dbccbd21a4caa99e619bcf7889dd9e28 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Mon, 22 Apr 2024 13:24:14 +0200 Subject: [PATCH 059/100] refactor: split _getDelegatedVotersAt into getDelegatedVoters and _getVotingPowerMultipleAt; rename getVotersStateAtVote to getVoterStateMultiple --- apps/voting/contracts/Voting.sol | 92 ++++++------ apps/voting/test/delegation.js | 235 +++++++++++++++---------------- 2 files changed, 166 insertions(+), 161 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 7219b52d7..6b6970147 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -431,28 +431,29 @@ contract Voting is IForwarder, AragonApp { } /** - * @notice Return the sliced list of delegated voters for `_delegate` and their voting power at the current block + * @notice Return the sliced list of delegated voters for `_delegate` * @param _delegate the address of the delegate * @param _offset the number of delegated voters from the start of the list to skip * @param _limit the number of delegated voters to return * @return the array of delegated voters - * @return the array of voting power of delegated voters */ - function getDelegatedVoters(address _delegate, uint256 _offset, uint256 _limit) external view returns (address[] memory, uint256[] memory) { - return _getDelegatedVotersAt(_delegate, _offset, _limit, getBlockNumber64()); - } + function getDelegatedVoters(address _delegate, uint256 _offset, uint256 _limit) external view returns (address[] memory voters) { + require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); + require(_limit > 0, ERROR_INVALID_LIMIT); + uint256 delegatedVotersCount = delegatedVoters[_delegate].addresses.length; + if (delegatedVotersCount == 0) { + return voters; + } + require(_offset < delegatedVotersCount, ERROR_INVALID_OFFSET); - /** - * @notice Return the sliced list of delegated voters for `_delegate` and their voting power at the `_voteId` snapshot block - * @param _delegate the address of the delegate - * @param _offset the number of delegated voters from the start of the list to skip - * @param _limit the number of delegated voters to return - * @return the array of delegated voters - * @return the array of voting power of delegated voters - */ - function getDelegatedVotersAtVote(address _delegate, uint256 _offset, uint256 _limit, uint256 _voteId) external view voteExists(_voteId) returns (address[] memory, uint256[] memory) { - Vote storage vote_ = votes[_voteId]; - return _getDelegatedVotersAt(_delegate, _offset, _limit, vote_.snapshotBlock); + uint256 returnCount = _offset.add(_limit) > delegatedVotersCount ? delegatedVotersCount.sub(_offset) : _limit; + voters = new address[](returnCount); + address voter; + for (uint256 i = 0; i < returnCount; ++i) { + voter = delegatedVoters[_delegate].addresses[_offset + i]; + voters[i] = voter; + } + return voters; } /** @@ -476,10 +477,11 @@ contract Voting is IForwarder, AragonApp { /** * @notice Return the list of `VoterState` for the `_voters` for the vote #`_voteId` - * @param _voters the list of voters * @param _voteId Vote identifier + * @param _voters list of voters + * @return the array of voter states */ - function getVotersStateAtVote(uint256 _voteId, address[] _voters) external view voteExists(_voteId) returns (VoterState[] memory voterStatesList) { + function getVoterStateMultiple(uint256 _voteId, address[] _voters) external view voteExists(_voteId) returns (VoterState[] memory voterStatesList) { uint256 votersCount = _voters.length; voterStatesList = new VoterState[](votersCount); Vote storage vote_ = votes[_voteId]; @@ -488,6 +490,26 @@ contract Voting is IForwarder, AragonApp { } } + /** + * @notice Return the voting power of the `_voters` at the current block + * @param _voters list of voters + * @return the array of governance token balances + */ + function getVotingPowerMultiple(address[] _voters) external view returns (uint256[] memory balances) { + return _getVotingPowerMultipleAt(_voters, getBlockNumber64()); + } + + /** + * @notice Return the voting power of the `_voters` at the vote #`_voteId` snapshot block + * @param _voteId Vote identifier + * @param _voters list of voters + * @return the array of governance token balances + */ + function getVotingPowerMultipleAtVote(uint256 _voteId, address[] _voters) external view voteExists(_voteId) returns (uint256[] memory balances) { + Vote storage vote_ = votes[_voteId]; + return _getVotingPowerMultipleAt(_voters, vote_.snapshotBlock); + } + // Internal fns /** @@ -615,33 +637,17 @@ contract Voting is IForwarder, AragonApp { } /** - * @notice Return the sliced list of delegated voters for `_delegate` and their voting power at the `_blockNumber` - * @param _delegate the address of the delegate - * @param _offset the number of delegated voters from the start of the list to skip - * @param _limit the number of delegated voters to return - * @param _blockNumber the block number to get the voting power - * @return the array of delegated voters - * @return the array of voting power of delegated voters + * @dev internal function to get the voting power of the `_voters` at the `_blockNumber` + * @param _voters list of voters + * @param _blockNumber target block number */ - function _getDelegatedVotersAt(address _delegate, uint256 _offset, uint256 _limit, uint256 _blockNumber) internal view returns (address[] memory votersList, uint256[] memory votingPowerList) { - require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); - require(_limit > 0, ERROR_INVALID_LIMIT); - uint256 delegatedVotersCount = delegatedVoters[_delegate].addresses.length; - if (delegatedVotersCount == 0) { - return (votersList, votingPowerList); - } - require(_offset < delegatedVotersCount, ERROR_INVALID_OFFSET); - - uint256 returnCount = _offset.add(_limit) > delegatedVotersCount ? delegatedVotersCount.sub(_offset) : _limit; - votersList = new address[](returnCount); - votingPowerList = new uint256[](returnCount); - address voter; - for (uint256 i = 0; i < returnCount; ++i) { - voter = delegatedVoters[_delegate].addresses[_offset + i]; - votersList[i] = voter; - votingPowerList[i] = token.balanceOfAt(voter, _blockNumber); + function _getVotingPowerMultipleAt(address[] _voters, uint256 _blockNumber) internal view returns (uint256[] memory balances) { + uint256 votersCount = _voters.length; + balances = new uint256[](votersCount); + for (uint256 i = 0; i < votersCount; ++i) { + balances[i] = token.balanceOfAt(_voters[i], _blockNumber); } - return (votersList, votingPowerList); + return balances; } /** diff --git a/apps/voting/test/delegation.js b/apps/voting/test/delegation.js index bfe0e9928..70244423b 100644 --- a/apps/voting/test/delegation.js +++ b/apps/voting/test/delegation.js @@ -164,7 +164,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 }) it('voter can set delegate', async () => { - let delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] + let delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 1) assert.equal(delegatedVoters.length, 0, 'delegate1 should not be a delegate of anyone') const tx = await voting.assignDelegate(delegate1, {from: holder29}) @@ -176,17 +176,17 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const delegate = await voting.getDelegate(holder29) assert.equal(delegate, delegate1, 'holder29 should have delegate1 as a delegate') - delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] + delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 1) assertArraysEqualAsSets(delegatedVoters, [holder29], 'delegate1 should be a delegate of holder29') }) it('voter can remove delegate', async () => { - let delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] + let delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 1) assert.equal(delegatedVoters.length, 0, 'delegate1 should not be a delegate of anyone') await voting.assignDelegate(delegate1, {from: holder29}) - delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] + delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 1) assertArraysEqualAsSets(delegatedVoters, [holder29], 'delegate1 should be a delegate of holder29') const tx = await voting.unassignDelegate({from: holder29}) @@ -194,12 +194,12 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 expectedArgs: {voter: holder29, unassignedDelegate: delegate1} }) assertAmountOfEvents(tx, 'UnassignDelegate', {expectedAmount: 1}) - delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] + delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 1) assertArraysEqualAsSets(delegatedVoters, [], 'delegate1 should not be a delegate of anyone') }) it('voters can remove delegate', async () => { - let delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] + let delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 1) assert.equal(delegatedVoters.length, 0, 'delegate1 should not be a delegate of anyone') await voting.assignDelegate(delegate1, {from: holder20}) @@ -211,7 +211,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 expectedArgs: {voter: holder29, unassignedDelegate: delegate1} }) assertAmountOfEvents(tx1, 'UnassignDelegate', {expectedAmount: 1}) - delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 5))[0] + delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 5) assertArraysEqualAsSets(delegatedVoters, [holder20, holder51], 'delegate1 have holder20 and holder51 as a delegated voters') const tx2 = await voting.unassignDelegate({from: holder51}) @@ -219,14 +219,14 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 expectedArgs: {voter: holder51, unassignedDelegate: delegate1} }) assertAmountOfEvents(tx2, 'UnassignDelegate', {expectedAmount: 1}) - delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] + delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 1) assertArraysEqualAsSets(delegatedVoters, [holder20], 'delegate1 have only holder20 as a delegated voter') }) it('voter can change delegate', async () => { - let delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] + let delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 1) assert.equal(delegatedVoters.length, 0, 'delegate1 should not be a delegate of anyone') - delegatedVoters = (await voting.getDelegatedVoters(delegate2, 0, 1))[0] + delegatedVoters = await voting.getDelegatedVoters(delegate2, 0, 1) assert.equal(delegatedVoters.length, 0, 'delegate2 should not be a delegate of anyone') await voting.assignDelegate(delegate1, {from: holder29}) @@ -234,14 +234,14 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 await voting.assignDelegate(delegate2, {from: holder29}) - const delegatedVotersDelegate1 = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] + const delegatedVotersDelegate1 = await voting.getDelegatedVoters(delegate1, 0, 1) assertArraysEqualAsSets(delegatedVotersDelegate1, [], 'delegate1 should not be a delegate of anyone') - const delegatedVotersDelegate2 = (await voting.getDelegatedVoters(delegate2, 0, 2))[0] + const delegatedVotersDelegate2 = await voting.getDelegatedVoters(delegate2, 0, 2) assertArraysEqualAsSets(delegatedVotersDelegate2, [holder29, holder51], 'delegate2 should be a delegate of holder29 and holder51') }) it('delegate can manage several voters', async () => { - let delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 1))[0] + let delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 1) assert.equal(delegatedVoters.length, 0, 'delegate1 should not be a delegate of anyone') const tx1 = await voting.assignDelegate(delegate1, {from: holder29}) @@ -256,7 +256,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 }) assertAmountOfEvents(tx2, 'AssignDelegate', {expectedAmount: 1}) - delegatedVoters = (await voting.getDelegatedVoters(delegate1, 0, 2))[0] + delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 2) assertArraysEqualAsSets(delegatedVoters, [holder29, holder51], 'delegate1 should be a delegate of holder29 and holder51') }) @@ -273,15 +273,15 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [holder1,holder20]) + const delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 3) + assertArraysEqualAsSets(delegatedVoters, [holder1,holder20]) - for (const holder of delegatedVotersData[0]) { + for (const holder of delegatedVoters) { await attemptVoteFor(voteId, false, holder, delegate1) } await verifyVoteYN(voteId, 0, LDO1.add(LDO20)) - const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + const voterState = await voting.getVoterStateMultiple(voteId, delegatedVoters) assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) }) @@ -297,14 +297,14 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) + const delegatedVoters = await voting.getDelegatedVoters(delegate2, 0, 3) - assertArraysEqualAsSets(delegatedVotersData[0], [holder29,holder51]) + assertArraysEqualAsSets(delegatedVoters, [holder29,holder51]) - await attemptVoteForMultiple(voteId, true, delegatedVotersData[0], delegate2) + await attemptVoteForMultiple(voteId, true, delegatedVoters, delegate2) await verifyVoteYN(voteId, LDO51.add(LDO29), 0) - const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + const voterState = await voting.getVoterStateMultiple(voteId, delegatedVoters) assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) }) @@ -319,15 +319,15 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [holder1, holder20]) + const delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 3) + assertArraysEqualAsSets(delegatedVoters, [holder1, holder20]) await attemptVoteFor(voteId, false, holder1, delegate1) await verifyVoteYN(voteId, 0, LDO1) - const voterStateHolder1 = await voting.getVotersStateAtVote(voteId, [holder1]) - const voterStateHolder20 = await voting.getVotersStateAtVote(voteId, [holder20]) + const voterStateHolder1 = await voting.getVoterStateMultiple(voteId, [holder1]) + const voterStateHolder20 = await voting.getVoterStateMultiple(voteId, [holder20]) assertArraysEqualAsSets(voterStateHolder1, [VOTER_STATE.DELEGATE_NAY.toString()]) assertArraysEqualAsSets(voterStateHolder20, [VOTER_STATE.ABSENT.toString()]) @@ -343,16 +343,16 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) + const delegatedVoters = await voting.getDelegatedVoters(delegate2, 0, 3) - assertArraysEqualAsSets(delegatedVotersData[0], [ holder29, holder51 ]) + assertArraysEqualAsSets(delegatedVoters, [ holder29, holder51 ]) await attemptVoteForMultiple(voteId, true, [holder29], delegate2) await verifyVoteYN(voteId, LDO29, 0) - const voterStateHolder29 = await voting.getVotersStateAtVote(voteId, [holder29]) - const voterStateHolder51 = await voting.getVotersStateAtVote(voteId, [holder51]) + const voterStateHolder29 = await voting.getVoterStateMultiple(voteId, [holder29]) + const voterStateHolder51 = await voting.getVoterStateMultiple(voteId, [holder51]) assertArraysEqualAsSets(voterStateHolder29, [VOTER_STATE.DELEGATE_YEA.toString()]) assertArraysEqualAsSets(voterStateHolder51, [VOTER_STATE.ABSENT.toString()]) @@ -366,20 +366,20 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [ holder1 ]) + const delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 3) + assertArraysEqualAsSets(delegatedVoters, [ holder1 ]) const supports = false await attemptVoteFor(voteId, supports, holder, delegate1) - const delegatedVoterState = await voting.getVotersStateAtVote(voteId, [holder]) + const delegatedVoterState = await voting.getVoterStateMultiple(voteId, [holder]) assertArraysEqualAsSets(delegatedVoterState, [VOTER_STATE.DELEGATE_NAY.toString()]) await vote( voteId, !supports, false, holder) await verifyVoteYN(voteId, LDO1, 0) - const voterState = await voting.getVotersStateAtVote(voteId, [holder]) + const voterState = await voting.getVoterStateMultiple(voteId, [holder]) assertArraysEqualAsSets(voterState, [VOTER_STATE.YEA.toString()]) }) @@ -391,19 +391,19 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [holder29]) + const delegatedVoters = await voting.getDelegatedVoters(delegate2, 0, 3) + assertArraysEqualAsSets(delegatedVoters, [holder29]) const supports = true await attemptVoteForMultiple(voteId, supports, [holder], delegate2) - const delegatedVoterState = await voting.getVotersStateAtVote(voteId, [holder]) + const delegatedVoterState = await voting.getVoterStateMultiple(voteId, [holder]) assertArraysEqualAsSets(delegatedVoterState, [VOTER_STATE.DELEGATE_YEA.toString()]) await vote(voteId, !supports, false, holder) await verifyVoteYN(voteId, 0 , LDO29) - const voterState = await voting.getVotersStateAtVote(voteId, [holder]) + const voterState = await voting.getVoterStateMultiple(voteId, [holder]) assertArraysEqualAsSets(voterState, [VOTER_STATE.NAY.toString()]) }) @@ -419,14 +419,14 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 assert.equal(phase, MAIN_PHASE) await assignDelegate(delegate1, holder) - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [ holder1 ]) + const delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 3) + assertArraysEqualAsSets(delegatedVoters, [ holder1 ]) await attemptVoteFor(voteId, false, holder, delegate1) await verifyVoteYN(voteId, 0, LDO1) - const voterState = await voting.getVotersStateAtVote(voteId, [holder]) + const voterState = await voting.getVoterStateMultiple(voteId, [holder]) assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) }) @@ -442,14 +442,14 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 assert.equal(phase, MAIN_PHASE) await assignDelegate(delegate2, holder) - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [holder29]) + const delegatedVoters = await voting.getDelegatedVoters(delegate2, 0, 3) + assertArraysEqualAsSets(delegatedVoters, [holder29]) await attemptVoteForMultiple(voteId, true, [holder], delegate2) await verifyVoteYN(voteId, LDO29, 0) - const voterState = await voting.getVotersStateAtVote(voteId, [holder]) + const voterState = await voting.getVoterStateMultiple(voteId, [holder]) assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) }) @@ -464,8 +464,8 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 assert.equal(phase, MAIN_PHASE) await assignDelegate(delegate2, holder) - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [ ]) + const delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 3) + assertArraysEqualAsSets(delegatedVoters, [ ]) await assertRevert(voting.attemptVoteFor(voteId, false, holder, {from: delegate1}), ERRORS.VOTING_CAN_NOT_VOTE_FOR) }) @@ -481,8 +481,8 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 assert.equal(phase, MAIN_PHASE) await assignDelegate(delegate1, holder) - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], []) + const delegatedVoters = await voting.getDelegatedVoters(delegate2, 0, 3) + assertArraysEqualAsSets(delegatedVoters, []) await assertRevert(voting.attemptVoteForMultiple(voteId, true, [holder], {from: delegate2}), ERRORS.VOTING_CAN_NOT_VOTE_FOR) @@ -502,15 +502,15 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 await token.generateTokens(holder1, bigExp(2, decimals)) await token.destroyTokens(holder20, bigExp(5, decimals)) - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [holder1,holder20]) + const delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 3) + assertArraysEqualAsSets(delegatedVoters, [holder1,holder20]) - for (const holder of delegatedVotersData[0]) { + for (const holder of delegatedVoters) { await attemptVoteFor(voteId, false, holder, delegate1) } await verifyVoteYN(voteId, 0, LDO1.add(LDO20)) - const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + const voterState = await voting.getVoterStateMultiple(voteId, delegatedVoters) assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) }) @@ -527,13 +527,13 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 await token.generateTokens(holder29, bigExp(2, decimals)) await token.destroyTokens(holder51, bigExp(5, decimals)) - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [holder29,holder51]) + const delegatedVoters = await voting.getDelegatedVoters(delegate2, 0, 3) + assertArraysEqualAsSets(delegatedVoters, [holder29,holder51]) - await attemptVoteForMultiple(voteId, true, delegatedVotersData[0], delegate2) + await attemptVoteForMultiple(voteId, true, delegatedVoters, delegate2) await verifyVoteYN(voteId, LDO51.add(LDO29), 0) - const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + const voterState = await voting.getVoterStateMultiple(voteId, delegatedVoters) assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) }) @@ -548,21 +548,21 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) + const delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 3) - assertArraysEqualAsSets(delegatedVotersData[0], [holder1, holder20]) + assertArraysEqualAsSets(delegatedVoters, [holder1, holder20]) - for (const holder of delegatedVotersData[0]) { + for (const holder of delegatedVoters) { await attemptVoteFor(voteId, false, holder, delegate1) } await verifyVoteYN(voteId,0, LDO1.add(LDO20)) - for (const holder of delegatedVotersData[0]) { + for (const holder of delegatedVoters) { await attemptVoteFor(voteId, true, holder, delegate1) } await verifyVoteYN(voteId, LDO1.add(LDO20), 0) - const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + const voterState = await voting.getVoterStateMultiple(voteId, delegatedVoters) assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) }) @@ -576,17 +576,17 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [holder29,holder51]) + const delegatedVoters = await voting.getDelegatedVoters(delegate2, 0, 3) + assertArraysEqualAsSets(delegatedVoters, [holder29,holder51]) - await attemptVoteForMultiple(voteId, true, delegatedVotersData[0], delegate2) + await attemptVoteForMultiple(voteId, true, delegatedVoters, delegate2) await verifyVoteYN(voteId, LDO51.add(LDO29), 0) - await attemptVoteForMultiple(voteId, false, delegatedVotersData[0], delegate2) + await attemptVoteForMultiple(voteId, false, delegatedVoters, delegate2) await verifyVoteYN(voteId, 0, LDO51.add(LDO29)) - const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + const voterState = await voting.getVoterStateMultiple(voteId, delegatedVoters) assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) }) @@ -601,10 +601,10 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [holder1, holder20]) + const delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 3) + assertArraysEqualAsSets(delegatedVoters, [holder1, holder20]) - for (const holder of delegatedVotersData[0]) { + for (const holder of delegatedVoters) { await attemptVoteFor(voteId, true, holder, delegate1) } @@ -614,12 +614,12 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const { phase } = await voting.getVote(voteId) assert.equal(phase, OBJECTION_PHASE) - for (const holder of delegatedVotersData[0]) { + for (const holder of delegatedVoters) { await attemptVoteFor(voteId, false, holder, delegate1) } await verifyVoteYN(voteId, 0, LDO1.add(LDO20)) - const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + const voterState = await voting.getVoterStateMultiple(voteId, delegatedVoters) assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) }) @@ -633,10 +633,10 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [holder29,holder51]) + const delegatedVoters = await voting.getDelegatedVoters(delegate2, 0, 3) + assertArraysEqualAsSets(delegatedVoters, [holder29,holder51]) - await attemptVoteForMultiple(voteId, true, delegatedVotersData[0], delegate2) + await attemptVoteForMultiple(voteId, true, delegatedVoters, delegate2) await voting.mockIncreaseTime(votingDuration - objectionPhase) await verifyVoteYN(voteId, LDO51.add(LDO29), 0) @@ -644,11 +644,11 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const { phase } = await voting.getVote(voteId) assert.equal(phase, OBJECTION_PHASE) - await attemptVoteForMultiple(voteId, false, delegatedVotersData[0], delegate2) + await attemptVoteForMultiple(voteId, false, delegatedVoters, delegate2) await verifyVoteYN(voteId, 0, LDO51.add(LDO29)) - const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + const voterState = await voting.getVoterStateMultiple(voteId, delegatedVoters) assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) }) @@ -663,10 +663,10 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [holder1, holder20]) + const delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 3) + assertArraysEqualAsSets(delegatedVoters, [holder1, holder20]) - for (const holder of delegatedVotersData[0]) { + for (const holder of delegatedVoters) { await attemptVoteFor(voteId, true, holder, delegate1) } @@ -687,7 +687,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 } await verifyVoteYN(voteId, 0, LDO1.add(LDO20)) - const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + const voterState = await voting.getVoterStateMultiple(voteId, delegatedVoters) assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) }) @@ -701,10 +701,10 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [holder29, holder51]) + const delegatedVoters = await voting.getDelegatedVoters(delegate2, 0, 3) + assertArraysEqualAsSets(delegatedVoters, [holder29, holder51]) - await attemptVoteForMultiple(voteId, true, delegatedVotersData[0], delegate2) + await attemptVoteForMultiple(voteId, true, delegatedVoters, delegate2) await voting.mockIncreaseTime(votingDuration - objectionPhase) await verifyVoteYN(voteId, LDO51.add(LDO29), 0) @@ -718,11 +718,11 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 await assignDelegate(delegate, holder) } - await attemptVoteForMultiple(voteId, false, delegatedVotersData[0], delegate1) + await attemptVoteForMultiple(voteId, false, delegatedVoters, delegate1) await verifyVoteYN(voteId, 0, LDO51.add(LDO29)) - const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + const voterState = await voting.getVoterStateMultiple(voteId, delegatedVoters) assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) }) @@ -737,10 +737,10 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [holder1, holder20]) + const delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 3) + assertArraysEqualAsSets(delegatedVoters, [holder1, holder20]) - for (const holder of delegatedVotersData[0]) { + for (const holder of delegatedVoters) { await attemptVoteFor(voteId, true, holder, delegate1) } @@ -750,12 +750,12 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const { phase } = await voting.getVote(voteId) assert.equal(phase, OBJECTION_PHASE) - for (const holder of delegatedVotersData[0]) { + for (const holder of delegatedVoters) { await vote(voteId, false, false, holder) } await verifyVoteYN(voteId, 0, LDO1.add(LDO20)) - const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + const voterState = await voting.getVoterStateMultiple(voteId, delegatedVoters) assertArraysEqualAsSets(voterState, [VOTER_STATE.NAY.toString()]) }) @@ -769,10 +769,10 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 3, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [holder29,holder51]) + const delegatedVoters = await voting.getDelegatedVoters(delegate2, 0, 3) + assertArraysEqualAsSets(delegatedVoters, [holder29,holder51]) - await attemptVoteForMultiple(voteId, true, delegatedVotersData[0], delegate2) + await attemptVoteForMultiple(voteId, true, delegatedVoters, delegate2) await voting.mockIncreaseTime(votingDuration - objectionPhase) await verifyVoteYN(voteId, LDO51.add(LDO29), 0) @@ -780,12 +780,12 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const { phase } = await voting.getVote(voteId) assert.equal(phase, OBJECTION_PHASE) - for (const holder of delegatedVotersData[0]) { + for (const holder of delegatedVoters) { await vote(voteId, false, false, holder) } await verifyVoteYN(voteId, 0, LDO51.add(LDO29)) - const voterState = await voting.getVotersStateAtVote(voteId, delegatedVotersData[0]) + const voterState = await voting.getVoterStateMultiple(voteId, delegatedVoters) assertArraysEqualAsSets(voterState, [VOTER_STATE.NAY.toString()]) }) @@ -801,17 +801,17 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate2, 0, 600, voteId) - assertArraysEqualAsSets(delegatedVotersData[0], [holder29, ...spamHolders, holder51]) + const delegatedVoters = await voting.getDelegatedVoters(delegate2, 0, 600) + assertArraysEqualAsSets(delegatedVoters, [holder29, ...spamHolders, holder51]) await attemptVoteForMultiple(voteId, true, [holder29, holder51], delegate2) await verifyVoteYN(voteId, LDO51.add(LDO29), 0) - const voterState = await voting.getVotersStateAtVote(voteId, [holder29, holder51]) + const voterState = await voting.getVoterStateMultiple(voteId, [holder29, holder51]) assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) - const voterStateSpam = await voting.getVotersStateAtVote(voteId, spamHolders) + const voterStateSpam = await voting.getVoterStateMultiple(voteId, spamHolders) assertArraysEqualAsSets(voterStateSpam, [VOTER_STATE.ABSENT.toString()]) }).timeout(60_000); @@ -875,44 +875,44 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 }) it(`if delegatedVoters array length is 0, return two empty arrays`, async () => { - const delegatdVotersData = await voting.getDelegatedVoters(nonHolder, 0, defaultLimit) - assert(delegatdVotersData[0].length === 0, 'votersList should be empty') - assert(delegatdVotersData[1].length === 0, 'votingPowerList should be empty') + const delegatedVoters = await voting.getDelegatedVoters(nonHolder, 0, defaultLimit) + const delegatedVotersVotingPower = await voting.getVotingPowerMultiple(delegatedVoters) + assert(delegatedVoters.length === 0, 'votersList should be empty') + assert(delegatedVotersVotingPower.length === 0, 'votingPowerList should be empty') }) it(`should return correct delegated voters data if offset + limit >= votersCount`, async () => { const offset = 2 const limit = 5 - const delegatedVotersData = await voting.getDelegatedVoters(delegate1, offset, limit) + const delegatedVoters = await voting.getDelegatedVoters(delegate1, offset, limit) const delegatedVotersCount = (await voting.getDelegatedVotersCount(delegate1)).toNumber() const delegatedVotersCountToReturn = delegatedVotersCount - offset - assert(delegatedVotersData[0].length === delegatedVotersCountToReturn) - assert(delegatedVotersData[1].length === delegatedVotersCountToReturn) + assert(delegatedVoters.length === delegatedVotersCountToReturn) const votersSlice = voters.slice(offset, delegatedVotersCount) const votersListSlice = votersSlice.map(voter => voter.account) - assertArraysEqualAsSets(delegatedVotersData[0], votersListSlice, 'votersList should be correct') + assertArraysEqualAsSets(delegatedVoters, votersListSlice, 'votersList should be correct') const votingPowerListSlice = votersSlice.map((voter) => voter.balance.toString()) - const votingPowerList = delegatedVotersData[1].map(votingPower => votingPower.toString()) + + const votingPowerList = (await voting.getVotingPowerMultiple(delegatedVoters)).map(votingPower => votingPower.toString()) assertArraysEqualAsSets(votingPowerList, votingPowerListSlice, 'votingPowerList should be correct') }) it(`should return correct delegated voters data if offset + limit < votersCount`, async () => { const offset = 1 const limit = 1 - const delegatedVotersData = await voting.getDelegatedVoters(delegate1, offset, limit) + const delegatedVoters = await voting.getDelegatedVoters(delegate1, offset, limit) - assert(delegatedVotersData[0].length === limit) - assert(delegatedVotersData[1].length === limit) + assert(delegatedVoters.length === limit) const votersSlice = voters.slice(offset, offset + limit) const votersListSlice = votersSlice.map(voter => voter.account) - assertArraysEqualAsSets(delegatedVotersData[0], votersListSlice, 'votersList should be correct') + assertArraysEqualAsSets(delegatedVoters, votersListSlice, 'votersList should be correct') const votingPowerListSlice = votersSlice.map((voter) => voter.balance.toString()) - const votingPowerList = delegatedVotersData[1].map(votingPower => votingPower.toString()) + const votingPowerList = (await voting.getVotingPowerMultiple(delegatedVoters)).map(votingPower => votingPower.toString()) assertArraysEqualAsSets(votingPowerList, votingPowerListSlice, 'votingPowerList should be correct') }) @@ -981,12 +981,12 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 metadata = getEventArgument(receipt, 'StartVote', 'metadata') }) - it(`getVotersStateAtVote`, async () => { + it(`getVoterStateMultiple`, async () => { await voting.vote(voteId, true, false, { from: holder20 }) await voting.vote(voteId, false, false, { from: holder29 }) await voting.attemptVoteForMultiple(voteId, false, [holder51], {from: delegate1}) - const votersState = await voting.getVotersStateAtVote(voteId, [holder20, holder29, holder51]) + const votersState = await voting.getVoterStateMultiple(voteId, [holder20, holder29, holder51]) assert.equal(votersState[0], VOTER_STATE.YEA, `holder20 should have 'yea' state`) assert.equal(votersState[1], VOTER_STATE.NAY, `holder29 should have 'nay' state`) assert.equal(votersState[2], VOTER_STATE.DELEGATE_NAY, `holder51 should have 'delegateNay' state`) @@ -1074,9 +1074,8 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 }) it(`successful vote for multiple`, async () => { - const delegatedVotersData = await voting.getDelegatedVotersAtVote(delegate1, 0, 100, voteId) - const delegatedVotersAddresses = delegatedVotersData[0] - const delegatedVotersVotingPower = delegatedVotersData[1] + const delegatedVotersAddresses = await voting.getDelegatedVoters(delegate1, 0, 100) + const delegatedVotersVotingPower = await voting.getVotingPowerMultipleAtVote(voteId, delegatedVotersAddresses) const filteredDelegatedVoters = [] for (let i = 0; i < delegatedVotersAddresses.length; i++) { const votingPower = delegatedVotersVotingPower[i] @@ -1104,7 +1103,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 } // Check voters' state - const votersState = await voting.getVotersStateAtVote(voteId, filteredDelegatedVotersAddresses) + const votersState = await voting.getVoterStateMultiple(voteId, filteredDelegatedVotersAddresses) votersState.every((state) => { assert.equal(state, VOTER_STATE.DELEGATE_NAY.toString(), `voter should have 'delegateNay' state`) }) From 5d01f379ce314fcc413316738903b657d75f85f6 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Mon, 22 Apr 2024 18:59:31 +0200 Subject: [PATCH 060/100] refactor: move balanceOfAt call up to `vote` and `attemptVoteForMultiple` --- apps/voting/contracts/Voting.sol | 45 ++++++++++++++------------------ 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 6b6970147..4148fffce 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -226,8 +226,10 @@ contract Voting is IForwarder, AragonApp { function vote(uint256 _voteId, bool _supports, bool /* _executesIfDecided_deprecated */) external voteExists(_voteId) { Vote storage vote_ = votes[_voteId]; require(_isValidPhaseToVote(vote_, _supports), ERROR_CAN_NOT_VOTE); - require(_hasVotingPower(vote_, msg.sender), ERROR_NO_VOTING_POWER); - _vote(_voteId, _supports, msg.sender, false); + // This could re-enter, though we can assume the governance token is not malicious + uint256 votingPower = token.balanceOfAt(msg.sender, vote_.snapshotBlock); + require(votingPower > 0, ERROR_NO_VOTING_POWER); + _vote(_voteId, _supports, msg.sender, false, votingPower); } /** @@ -279,11 +281,14 @@ contract Voting is IForwarder, AragonApp { bool votedForAtLeastOne = false; address voter; + uint256 votingPower; for (uint256 i = 0; i < _voters.length; ++i) { voter = _voters[i]; - require(_hasVotingPower(vote_, voter), ERROR_NO_VOTING_POWER); + // This could re-enter, though we can assume the governance token is not malicious + votingPower = token.balanceOfAt(voter, vote_.snapshotBlock); + require(votingPower > 0, ERROR_NO_VOTING_POWER); if (_canVoteFor(vote_, voter, msg.sender)) { - _vote(_voteId, _supports, voter, true); + _vote(_voteId, _supports, voter, true, votingPower); votedForAtLeastOne = true; } } @@ -358,7 +363,8 @@ contract Voting is IForwarder, AragonApp { */ function canVote(uint256 _voteId, address _voter) external view voteExists(_voteId) returns (bool) { Vote storage vote_ = votes[_voteId]; - return _isVoteOpen(vote_) && _hasVotingPower(vote_, _voter); + uint256 votingPower = token.balanceOfAt(_voter, vote_.snapshotBlock); + return _isVoteOpen(vote_) && votingPower > 0; } /** @@ -544,36 +550,33 @@ contract Voting is IForwarder, AragonApp { * @param _voter The address of the voter * @param _isDelegate Whether the voter is a delegate */ - function _vote(uint256 _voteId, bool _supports, address _voter, bool _isDelegate) internal { + function _vote(uint256 _voteId, bool _supports, address _voter, bool _isDelegate, uint256 _votingPower) internal { Vote storage vote_ = votes[_voteId]; - - // This could re-enter, though we can assume the governance token is not malicious - uint256 voterStake = token.balanceOfAt(_voter, vote_.snapshotBlock); VoterState state = vote_.voters[_voter]; // If voter had previously voted, decrease count if (state == VoterState.Yea || state == VoterState.DelegateYea) { - vote_.yea = vote_.yea.sub(voterStake); + vote_.yea = vote_.yea.sub(_votingPower); } else if (state == VoterState.Nay || state == VoterState.DelegateNay) { - vote_.nay = vote_.nay.sub(voterStake); + vote_.nay = vote_.nay.sub(_votingPower); } if (_supports) { - vote_.yea = vote_.yea.add(voterStake); + vote_.yea = vote_.yea.add(_votingPower); vote_.voters[_voter] = _isDelegate ? VoterState.DelegateYea : VoterState.Yea; } else { - vote_.nay = vote_.nay.add(voterStake); + vote_.nay = vote_.nay.add(_votingPower); vote_.voters[_voter] = _isDelegate ? VoterState.DelegateNay : VoterState.Nay; } - emit CastVote(_voteId, _voter, _supports, voterStake); + emit CastVote(_voteId, _voter, _supports, _votingPower); if (_getVotePhase(vote_) == VotePhase.Objection) { - emit CastObjection(_voteId, _voter, voterStake); + emit CastObjection(_voteId, _voter, _votingPower); } if (_isDelegate) { - emit CastVoteAsDelegate(_voteId, msg.sender, _voter, _supports, voterStake); + emit CastVoteAsDelegate(_voteId, msg.sender, _voter, _supports, _votingPower); } } @@ -713,16 +716,6 @@ contract Voting is IForwarder, AragonApp { return state != VoterState.Yea && state != VoterState.Nay; } - /** - * @dev Internal function to get the voter's token balance at the vote's snapshot block - * @param vote_ The queried vote - * @param _voter address of the voter - * @return The voter's token balance at the vote's snapshot block - */ - function _hasVotingPower(Vote storage vote_, address _voter) internal view returns (bool) { - return token.balanceOfAt(_voter, vote_.snapshotBlock) > 0; - } - /** * @dev Internal function to get the current phase of the vote. It assumes the queried vote exists. * @param vote_ The queried vote From 9cbaf2a9f98013a14490a788d7d40b6716cfaf61 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Mon, 22 Apr 2024 18:59:48 +0200 Subject: [PATCH 061/100] test: add gas benchmark tests --- apps/voting/test/delegation.js | 56 +++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/apps/voting/test/delegation.js b/apps/voting/test/delegation.js index 70244423b..4556d0ded 100644 --- a/apps/voting/test/delegation.js +++ b/apps/voting/test/delegation.js @@ -47,7 +47,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 aclP = acl }) - context('voting delegate', () => { + context('simple delegation scenarios', () => { const neededSupport = pct16(50) const minimumAcceptanceQuorum = pct16(20) const decimals = 18 @@ -1143,4 +1143,58 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 }) }) + + context.skip('Gas estimation tests (should be skipped)', () => { + let script, voteId, creator, metadata + + const neededSupport = pct16(50) + const minimumAcceptanceQuorum = pct16(20) + const decimals = 18 + + beforeEach(async () => { + token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', decimals, 'n', true) // empty parameters minime + + await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, objectionPhase) + await token.generateTokens(holder51, bigExp(51, decimals)) + + for (const holder of spamHolders) { + await token.generateTokens(holder, bigExp(1, decimals)) + await voting.assignDelegate(delegate1, {from: holder}) + } + + executionTarget = await ExecutionTarget.new() + + const action = {to: executionTarget.address, calldata: executionTarget.contract.methods.execute().encodeABI()} + script = encodeCallScript([action, action]) + + const receipt = await voting.methods['newVote(bytes,string)'](script, 'metadata', {from: holder51}) + voteId = getEventArgument(receipt, 'StartVote', 'voteId') + creator = getEventArgument(receipt, 'StartVote', 'creator') + metadata = getEventArgument(receipt, 'StartVote', 'metadata') + }) + + it(`voting for 1`, async () => { + let voter = spamHolders[0] + let tx = await voting.attemptVoteFor(voteId, false, voter, {from: delegate1}) + console.log('Gas used for voting for 1:', tx.receipt.gasUsed) + + voter = spamHolders[1] + tx = await voting.vote(voteId, true, false, {from: voter}) + console.log('Gas used for a single .vote()', tx.receipt.gasUsed) + + }) + + it(`voting for 10`, async () => { + const voters = spamHolders.slice(0, 10) + const tx = await voting.attemptVoteForMultiple(voteId, false, voters, {from: delegate1}) + console.log('Gas used for voting for 10:', tx.receipt.gasUsed) + }) + + it(`voting for 100`, async () => { + const voters = spamHolders.slice(0, 100) + const tx = await voting.attemptVoteForMultiple(voteId, false, voters, {from: delegate1}) + console.log('Gas used for voting for 100:', tx.receipt.gasUsed) + }) + + }) }) From 32a3dd239c42b642c2cbba3065bbf66e2f4a61b0 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Mon, 22 Apr 2024 21:24:37 +0200 Subject: [PATCH 062/100] refactor: move `CastVoteAsDelegate` to `attemptVoteForMultiple` --- apps/voting/contracts/Voting.sol | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 4148fffce..55fea4a4e 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -98,7 +98,7 @@ contract Voting is IForwarder, AragonApp { event ChangeObjectionPhaseTime(uint64 objectionPhaseTime); event AssignDelegate(address indexed voter, address indexed delegate); event UnassignDelegate(address indexed voter, address indexed unassignedDelegate); - event CastVoteAsDelegate(uint256 indexed voteId, address indexed delegate, address indexed voter, bool supports, uint256 stake); + event CastVoteAsDelegate(uint256 indexed voteId, address indexed delegate, bool supports, address[] voters, bool[] votedFor); modifier voteExists(uint256 _voteId) { require(_voteId < votesLength, ERROR_NO_VOTE); @@ -278,10 +278,11 @@ contract Voting is IForwarder, AragonApp { function attemptVoteForMultiple(uint256 _voteId, bool _supports, address[] _voters) public voteExists(_voteId) { Vote storage vote_ = votes[_voteId]; require(_isValidPhaseToVote(vote_, _supports), ERROR_CAN_NOT_VOTE); - bool votedForAtLeastOne = false; + bool votedForAtLeastOne = false; address voter; uint256 votingPower; + bool[] memory votedFor = new bool[](_voters.length); for (uint256 i = 0; i < _voters.length; ++i) { voter = _voters[i]; // This could re-enter, though we can assume the governance token is not malicious @@ -289,11 +290,13 @@ contract Voting is IForwarder, AragonApp { require(votingPower > 0, ERROR_NO_VOTING_POWER); if (_canVoteFor(vote_, voter, msg.sender)) { _vote(_voteId, _supports, voter, true, votingPower); + votedFor[i] = true; votedForAtLeastOne = true; } } - require(votedForAtLeastOne, ERROR_CAN_NOT_VOTE_FOR); + + emit CastVoteAsDelegate(_voteId, msg.sender, _supports, _voters, votedFor); } /** @@ -574,10 +577,6 @@ contract Voting is IForwarder, AragonApp { if (_getVotePhase(vote_) == VotePhase.Objection) { emit CastObjection(_voteId, _voter, _votingPower); } - - if (_isDelegate) { - emit CastVoteAsDelegate(_voteId, msg.sender, _voter, _supports, _votingPower); - } } /** From 8c6dcec8b8f08bff554fe9700e8af1d5e12d8178 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Mon, 22 Apr 2024 21:25:03 +0200 Subject: [PATCH 063/100] test: reorder gas tests --- apps/voting/test/delegation.js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/apps/voting/test/delegation.js b/apps/voting/test/delegation.js index 4556d0ded..2727cfcee 100644 --- a/apps/voting/test/delegation.js +++ b/apps/voting/test/delegation.js @@ -1173,15 +1173,16 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 metadata = getEventArgument(receipt, 'StartVote', 'metadata') }) + it(`voting without delegation`, async () => { + const voter = spamHolders[0] + const tx = await voting.vote(voteId, true, false, {from: voter}) + console.log('Gas used for a voting without delegation:', tx.receipt.gasUsed) + }) + it(`voting for 1`, async () => { - let voter = spamHolders[0] - let tx = await voting.attemptVoteFor(voteId, false, voter, {from: delegate1}) + const voter = spamHolders[0] + const tx = await voting.attemptVoteFor(voteId, false, voter, {from: delegate1}) console.log('Gas used for voting for 1:', tx.receipt.gasUsed) - - voter = spamHolders[1] - tx = await voting.vote(voteId, true, false, {from: voter}) - console.log('Gas used for a single .vote()', tx.receipt.gasUsed) - }) it(`voting for 10`, async () => { From 20ba1ea9f9b940af72defa3c33ad9170921e4886 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 23 Apr 2024 15:17:30 +0200 Subject: [PATCH 064/100] test: fix `CastVoteAsDelegate` checks --- apps/voting/test/delegation.js | 36 +++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/apps/voting/test/delegation.js b/apps/voting/test/delegation.js index 2727cfcee..7f365dab3 100644 --- a/apps/voting/test/delegation.js +++ b/apps/voting/test/delegation.js @@ -79,9 +79,9 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 assertEvent(tx, 'CastVote', { expectedArgs: {voteId: voteId, voter: holder, supports: supports, stake: initBalance[holder]} }) - assertEvent(tx, 'CastVoteAsDelegate', { - expectedArgs: {voteId: voteId, delegate: delegate, voter: holder, supports: supports, stake: initBalance[holder]} - }) + assertEvent(tx, 'CastVoteAsDelegate', { expectedArgs: {voteId, delegate, supports} }) + const votersFromEvent = getEventArgument(tx, 'CastVoteAsDelegate', 'voters') + assertArraysEqualAsSets([holder], votersFromEvent) assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 1}) } @@ -101,13 +101,12 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 index, expectedArgs: {voteId, voter: holder, supports, stake} }) - assertEvent(tx, 'CastVoteAsDelegate', { - index, - expectedArgs: {voteId, delegate, voter: holder, supports, stake} - }) } + assertEvent(tx, 'CastVoteAsDelegate', { expectedArgs: {voteId, delegate} }) + const votersFromEvent = getEventArgument(tx, 'CastVoteAsDelegate', 'voters') + assertArraysEqualAsSets(holders, votersFromEvent) assertAmountOfEvents(tx, 'CastVote', {expectedAmount: holders.length}) - assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: holders.length}) + assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 1}) } const vote = async (voteId, supports, exec, holder) => { const tx = await voting.vote(voteId, supports, exec, {from: holder}) @@ -265,7 +264,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 // they get the full list of voters with their balance snapshot and vote states of the given voting, // then they vote for that list. it('delegate can manage several voters and vote for all (voteFor)', async () => { - const delegateList= [ [delegate1, holder1], [delegate1, holder20] ] + const delegateList = [ [delegate1, holder1], [delegate1, holder20] ] for (const [delegate, holder] of delegateList) { await assignDelegate(delegate, holder) @@ -274,7 +273,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) const delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 3) - assertArraysEqualAsSets(delegatedVoters, [holder1,holder20]) + assertArraysEqualAsSets(delegatedVoters, [holder1, holder20]) for (const holder of delegatedVoters) { await attemptVoteFor(voteId, false, holder, delegate1) @@ -1041,7 +1040,9 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 1}) assertEvent(tx, 'CastVote', {expectedArgs: {voteId, voter: holder20, supports: true}}) - assertEvent(tx, 'CastVoteAsDelegate', {expectedArgs: {voteId, delegate: delegate1, voter: holder20, supports: true}}) + assertEvent(tx, 'CastVoteAsDelegate', {expectedArgs: {voteId, delegate: delegate1, supports: true}}) + const votersFromEvent = getEventArgument(tx, 'CastVoteAsDelegate', 'voters') + assertArraysEqualAsSets([holder20, holder29], votersFromEvent) assert.equal(await voting.getVoterState(voteId, holder29), VOTER_STATE.NAY, `holder29 should have 'nay' state`) assert.equal(await voting.getVoterState(voteId, holder20), VOTER_STATE.DELEGATE_YEA, `holder20 should have 'delegateYea' state`) @@ -1051,7 +1052,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const tx = await voting.attemptVoteForMultiple(voteId, false, [holder29, holder29], {from: delegate1}); assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 2}) - assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 2}) + assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 1}) assert.equal(await voting.getVoterState(voteId, holder29), VOTER_STATE.DELEGATE_NAY, `holder29 should have 'delegateNay' state`) const vote = await voting.getVote(voteId) assertBn(vote.nay, bigExp(29, decimals), 'nay should be 29') @@ -1093,14 +1094,16 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 // Check amount of events assertAmountOfEvents(tx, 'CastVote', {expectedAmount: filteredDelegatedVoters.length}) - assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: filteredDelegatedVoters.length}) + assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 1}) // Check events content for (let i = 0; i < filteredDelegatedVoters.length; i++) { const {address, votingPower} = filteredDelegatedVoters[i] assertEvent(tx, 'CastVote', {index: i, expectedArgs: {voteId, voter: address, supports: false, stake: votingPower}}) - assertEvent(tx, 'CastVoteAsDelegate', {index: i, expectedArgs: {voteId, delegate: delegate1, voter: address, supports: false, stake: votingPower}}) } + assertEvent(tx, 'CastVoteAsDelegate', {expectedArgs: {voteId, delegate: delegate1, supports: false}}) + const votersFromEvent = getEventArgument(tx, 'CastVoteAsDelegate', 'voters') + assertArraysEqualAsSets(filteredDelegatedVoters, votersFromEvent) // Check voters' state const votersState = await voting.getVoterStateMultiple(voteId, filteredDelegatedVotersAddresses) @@ -1130,8 +1133,9 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 // Check events content assertEvent(tx, 'CastVote', { expectedArgs: {voteId, voter: holder29, supports: false, stake: holder29VP}}) - assertEvent(tx, 'CastVoteAsDelegate', { expectedArgs: {voteId, delegate: delegate1, voter: holder29, supports: false, stake: holder29VP}}) - + assertEvent(tx, 'CastVoteAsDelegate', { expectedArgs: {voteId, delegate: delegate1, supports: false}}) + const votersFromEvent = getEventArgument(tx, 'CastVoteAsDelegate', 'voters') + assertArraysEqualAsSets([holder29], votersFromEvent) // Check voter's state assert.equal(await voting.getVoterState(voteId, holder29), VOTER_STATE.DELEGATE_NAY, `holder29 should have 'delegateNay' state`) From 845a5dd25486ee4619c722e168be768811a9d96d Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Wed, 24 Apr 2024 17:23:31 +0200 Subject: [PATCH 065/100] refactor: remove `votedFor` param from `CastVoteAsDelegate`; rename `CastVoteAsDelegate` to `AttemptCastVoteAsDelegate` --- apps/voting/contracts/Voting.sol | 6 ++---- apps/voting/test/delegation.js | 34 ++++++++++++++++---------------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 55fea4a4e..6955a2456 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -98,7 +98,7 @@ contract Voting is IForwarder, AragonApp { event ChangeObjectionPhaseTime(uint64 objectionPhaseTime); event AssignDelegate(address indexed voter, address indexed delegate); event UnassignDelegate(address indexed voter, address indexed unassignedDelegate); - event CastVoteAsDelegate(uint256 indexed voteId, address indexed delegate, bool supports, address[] voters, bool[] votedFor); + event AttemptCastVoteAsDelegate(uint256 indexed voteId, address indexed delegate, address[] voters); modifier voteExists(uint256 _voteId) { require(_voteId < votesLength, ERROR_NO_VOTE); @@ -282,7 +282,6 @@ contract Voting is IForwarder, AragonApp { bool votedForAtLeastOne = false; address voter; uint256 votingPower; - bool[] memory votedFor = new bool[](_voters.length); for (uint256 i = 0; i < _voters.length; ++i) { voter = _voters[i]; // This could re-enter, though we can assume the governance token is not malicious @@ -290,13 +289,12 @@ contract Voting is IForwarder, AragonApp { require(votingPower > 0, ERROR_NO_VOTING_POWER); if (_canVoteFor(vote_, voter, msg.sender)) { _vote(_voteId, _supports, voter, true, votingPower); - votedFor[i] = true; votedForAtLeastOne = true; } } require(votedForAtLeastOne, ERROR_CAN_NOT_VOTE_FOR); - emit CastVoteAsDelegate(_voteId, msg.sender, _supports, _voters, votedFor); + emit AttemptCastVoteAsDelegate(_voteId, msg.sender, _voters); } /** diff --git a/apps/voting/test/delegation.js b/apps/voting/test/delegation.js index 7f365dab3..368d16f1b 100644 --- a/apps/voting/test/delegation.js +++ b/apps/voting/test/delegation.js @@ -77,13 +77,13 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const attemptVoteFor = async (voteId, supports, holder, delegate) => { const tx = await voting.attemptVoteFor(voteId, supports, holder, {from: delegate}) assertEvent(tx, 'CastVote', { - expectedArgs: {voteId: voteId, voter: holder, supports: supports, stake: initBalance[holder]} + expectedArgs: {voteId: voteId, voter: holder, supports, stake: initBalance[holder]} }) - assertEvent(tx, 'CastVoteAsDelegate', { expectedArgs: {voteId, delegate, supports} }) - const votersFromEvent = getEventArgument(tx, 'CastVoteAsDelegate', 'voters') + assertEvent(tx, 'AttemptCastVoteAsDelegate', { expectedArgs: {voteId, delegate} }) + const votersFromEvent = getEventArgument(tx, 'AttemptCastVoteAsDelegate', 'voters') assertArraysEqualAsSets([holder], votersFromEvent) assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) - assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'AttemptCastVoteAsDelegate', {expectedAmount: 1}) } const attemptVoteForMultiple = async (voteId, supports, holders, delegate) => { const tx = await voting.attemptVoteForMultiple(voteId, supports, holders, {from: delegate}) @@ -102,11 +102,11 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 expectedArgs: {voteId, voter: holder, supports, stake} }) } - assertEvent(tx, 'CastVoteAsDelegate', { expectedArgs: {voteId, delegate} }) - const votersFromEvent = getEventArgument(tx, 'CastVoteAsDelegate', 'voters') + assertEvent(tx, 'AttemptCastVoteAsDelegate', { expectedArgs: {voteId, delegate} }) + const votersFromEvent = getEventArgument(tx, 'AttemptCastVoteAsDelegate', 'voters') assertArraysEqualAsSets(holders, votersFromEvent) assertAmountOfEvents(tx, 'CastVote', {expectedAmount: holders.length}) - assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'AttemptCastVoteAsDelegate', {expectedAmount: 1}) } const vote = async (voteId, supports, exec, holder) => { const tx = await voting.vote(voteId, supports, exec, {from: holder}) @@ -1038,10 +1038,10 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const tx = await voting.attemptVoteForMultiple(voteId, true, [holder20, holder29], {from: delegate1}); assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) - assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'AttemptCastVoteAsDelegate', {expectedAmount: 1}) assertEvent(tx, 'CastVote', {expectedArgs: {voteId, voter: holder20, supports: true}}) - assertEvent(tx, 'CastVoteAsDelegate', {expectedArgs: {voteId, delegate: delegate1, supports: true}}) - const votersFromEvent = getEventArgument(tx, 'CastVoteAsDelegate', 'voters') + assertEvent(tx, 'AttemptCastVoteAsDelegate', {expectedArgs: {voteId, delegate: delegate1}}) + const votersFromEvent = getEventArgument(tx, 'AttemptCastVoteAsDelegate', 'voters') assertArraysEqualAsSets([holder20, holder29], votersFromEvent) assert.equal(await voting.getVoterState(voteId, holder29), VOTER_STATE.NAY, `holder29 should have 'nay' state`) @@ -1052,7 +1052,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const tx = await voting.attemptVoteForMultiple(voteId, false, [holder29, holder29], {from: delegate1}); assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 2}) - assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'AttemptCastVoteAsDelegate', {expectedAmount: 1}) assert.equal(await voting.getVoterState(voteId, holder29), VOTER_STATE.DELEGATE_NAY, `holder29 should have 'delegateNay' state`) const vote = await voting.getVote(voteId) assertBn(vote.nay, bigExp(29, decimals), 'nay should be 29') @@ -1094,15 +1094,15 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 // Check amount of events assertAmountOfEvents(tx, 'CastVote', {expectedAmount: filteredDelegatedVoters.length}) - assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'AttemptCastVoteAsDelegate', {expectedAmount: 1}) // Check events content for (let i = 0; i < filteredDelegatedVoters.length; i++) { const {address, votingPower} = filteredDelegatedVoters[i] assertEvent(tx, 'CastVote', {index: i, expectedArgs: {voteId, voter: address, supports: false, stake: votingPower}}) } - assertEvent(tx, 'CastVoteAsDelegate', {expectedArgs: {voteId, delegate: delegate1, supports: false}}) - const votersFromEvent = getEventArgument(tx, 'CastVoteAsDelegate', 'voters') + assertEvent(tx, 'AttemptCastVoteAsDelegate', {expectedArgs: {voteId, delegate: delegate1}}) + const votersFromEvent = getEventArgument(tx, 'AttemptCastVoteAsDelegate', 'voters') assertArraysEqualAsSets(filteredDelegatedVoters, votersFromEvent) // Check voters' state @@ -1127,14 +1127,14 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 // Check amount of events assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) - assertAmountOfEvents(tx, 'CastVoteAsDelegate', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'AttemptCastVoteAsDelegate', {expectedAmount: 1}) const holder29VP = bigExp(29, decimals) // Check events content assertEvent(tx, 'CastVote', { expectedArgs: {voteId, voter: holder29, supports: false, stake: holder29VP}}) - assertEvent(tx, 'CastVoteAsDelegate', { expectedArgs: {voteId, delegate: delegate1, supports: false}}) - const votersFromEvent = getEventArgument(tx, 'CastVoteAsDelegate', 'voters') + assertEvent(tx, 'AttemptCastVoteAsDelegate', { expectedArgs: {voteId, delegate: delegate1}}) + const votersFromEvent = getEventArgument(tx, 'AttemptCastVoteAsDelegate', 'voters') assertArraysEqualAsSets([holder29], votersFromEvent) // Check voter's state From 9076462fd953ce89653ce4fd47c06b903cbbf0cb Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Wed, 24 Apr 2024 18:20:18 +0200 Subject: [PATCH 066/100] docs: add comment with explanation of delegates voting checks usage --- apps/voting/contracts/Voting.sol | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 6955a2456..362956734 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -286,6 +286,10 @@ contract Voting is IForwarder, AragonApp { voter = _voters[i]; // This could re-enter, though we can assume the governance token is not malicious votingPower = token.balanceOfAt(voter, vote_.snapshotBlock); + + // Voting power check is not used the same way as `_canVoteFor` to have consistency between `attemptVoteForMultiple` and `vote`. + // Moreover, it's impossible to front-run a delegate by moving tokens before their voting attempt, + // but it is possible to front-run a delegate by unassigning them or voting before their voting attempt. require(votingPower > 0, ERROR_NO_VOTING_POWER); if (_canVoteFor(vote_, voter, msg.sender)) { _vote(_voteId, _supports, voter, true, votingPower); From d8f882a74be0a4afae2d6e667f9d4d3b08503959 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 30 Apr 2024 15:07:51 +0200 Subject: [PATCH 067/100] test: improve test coverage --- apps/voting/package.json | 13 ++-- apps/voting/test/delegation.js | 96 +++++++++++++++++++++++++++++- apps/voting/test/helpers/errors.js | 3 + apps/voting/test/voting.js | 42 +++++++++++++ 4 files changed, 146 insertions(+), 8 deletions(-) diff --git a/apps/voting/package.json b/apps/voting/package.json index aff0e2c38..9c1717b2f 100644 --- a/apps/voting/package.json +++ b/apps/voting/package.json @@ -33,21 +33,22 @@ "@aragon/os": "4.4.0" }, "devDependencies": { - "patch-package": "^6.4.7", "@aragon/contract-helpers-test": "^0.1.0", "@aragon/hardhat-aragon": "^1.0.0", "@aragon/hardhat-config": "^1.0.1", + "@nomicfoundation/hardhat-network-helpers": "^1.0.10", + "@nomiclabs/hardhat-ethers": "^2.0.2", + "@nomiclabs/hardhat-etherscan": "^3.1.8", "@nomiclabs/hardhat-ganache": "^2.0.1", "@nomiclabs/hardhat-truffle5": "^2.0.5", "@nomiclabs/hardhat-web3": "^2.0.0", - "@nomiclabs/hardhat-ethers": "^2.0.2", - "@nomiclabs/hardhat-etherscan": "^3.1.8", - "hardhat-abi-exporter": "^2.8.0", - "hardhat-gas-reporter": "^2.1.1", "chai": "^4.2.0", "ethers": "^5.4.7", "hardhat": "^2.22.2", - "solidity-coverage": "^0.7.9", + "hardhat-abi-exporter": "^2.8.0", + "hardhat-gas-reporter": "^2.1.1", + "patch-package": "^6.4.7", + "solidity-coverage": "^0.8.0", "solium": "^1.2.5", "web3": "^1.3.0" } diff --git a/apps/voting/test/delegation.js b/apps/voting/test/delegation.js index 368d16f1b..ee522d3bd 100644 --- a/apps/voting/test/delegation.js +++ b/apps/voting/test/delegation.js @@ -4,6 +4,7 @@ const { assertBn, assertRevert, assertAmountOfEvents, assertEvent } = require('@ const { pct16, bn, bigExp, getEventArgument, ZERO_ADDRESS } = require('@aragon/contract-helpers-test') const { newDao, installNewApp, encodeCallScript, ANY_ENTITY, EMPTY_CALLS_SCRIPT } = require('@aragon/contract-helpers-test/src/aragon-os') const { assert } = require('chai') +const { getStorageAt, setStorageAt, impersonateAccount } = require("@nomicfoundation/hardhat-network-helpers") const Voting = artifacts.require('VotingMock') @@ -179,6 +180,31 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 assertArraysEqualAsSets(delegatedVoters, [holder29], 'delegate1 should be a delegate of holder29') }) + it(`assignment fails if delegatedVoters array is overflown`, async () => { + const arrayLengthSlotIndex = 5 + const paddedAddress = ethers.utils.hexZeroPad(delegate1, 32) + const paddedSlot = ethers.utils.hexZeroPad(arrayLengthSlotIndex, 32) + const arrayLengthSlot = ethers.utils.solidityKeccak256(['address', 'uint256'], [paddedAddress, paddedSlot]) + + // Check that slot index is correct + let storage = await getStorageAt(voting.address, arrayLengthSlot) + assert(ethers.BigNumber.from(storage).eq(0), 'delegatedVoters array length should be 0') + + await voting.assignDelegate(delegate1, {from: holder29}) + storage = await getStorageAt(voting.address, arrayLengthSlot) + assert(ethers.BigNumber.from(storage).eq(1), 'delegatedVoters array length should be 1 after assignment') + + // Update slot value to max uint96 + const uint96Max = ethers.BigNumber.from(2).pow(96) + await setStorageAt(voting.address, arrayLengthSlot, uint96Max) + + // Check that revert is thrown when trying to assign a delegate + await assertRevert( + voting.assignDelegate(delegate1, {from: holder51}), + ERRORS.VOTING_MAX_DELEGATED_VOTERS_REACHED + ) + }) + it('voter can remove delegate', async () => { let delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 1) assert.equal(delegatedVoters.length, 0, 'delegate1 should not be a delegate of anyone') @@ -835,6 +861,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 account: holder29, balance: bigExp(29, decimals) }] + let voteId beforeEach(async () => { token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', decimals, 'n', true) // empty parameters minime @@ -847,6 +874,12 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 } executionTarget = await ExecutionTarget.new() + + const action = {to: executionTarget.address, calldata: executionTarget.contract.methods.execute().encodeABI()} + script = encodeCallScript([action, action]) + + const receipt = await voting.methods['newVote(bytes,string)'](script, 'metadata', {from: holder51}); + voteId = getEventArgument(receipt, 'StartVote', 'voteId') }) it('should return correct delegated voters count', async () => { @@ -868,8 +901,15 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 it(`revert if "_limit" is 0`, async () => { await assertRevert( - voting.getDelegatedVoters(ZERO_ADDRESS, 0, defaultLimit), - ERRORS.VOTING_ZERO_ADDRESS_PASSED + voting.getDelegatedVoters(delegate1, 0, 0), + ERRORS.VOTING_INVALID_LIMIT + ) + }) + + it(`revert if "_offset" more or equal than delegated voters count`, async () => { + await assertRevert( + voting.getDelegatedVoters(delegate1, 10, 1), + ERRORS.VOTING_INVALID_OFFSET ) }) @@ -931,6 +971,31 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const delegate = await voting.getDelegate(holder1) assert.equal(delegate, delegate1, 'should return delegate1 address') }) + + it(`voting power getters`, async () => { + const initialVotingPower = voters.map(v => v.balance.toString()) + const votersAddresses = voters.map(v => v.account) + + await assertRevert(voting.getVotingPowerMultipleAtVote(voteId + 1, votersAddresses), ERRORS.VOTING_NO_VOTE) + + const currentVotingPower = await voting.getVotingPowerMultiple(votersAddresses) + assertArraysEqualAsSets(currentVotingPower, initialVotingPower, 'current voting power values should match') + + const updatedVoterIndex = 0 + const vpAddition = bigExp(1, decimals) + await token.generateTokens(voters[updatedVoterIndex].account, vpAddition) + const updatedVotingPowerToCompare = voters.map((v, i) => { + if (i === updatedVoterIndex) { + return v.balance.add(vpAddition).toString() + } + return v.balance.toString() + }) + const updatedVotingPower = await voting.getVotingPowerMultiple(votersAddresses) + assertArraysEqualAsSets(updatedVotingPower, updatedVotingPowerToCompare, 'current voting power values should match after update') + + const votingPowerAtVote = await voting.getVotingPowerMultipleAtVote(voteId, votersAddresses) + assertArraysEqualAsSets(votingPowerAtVote, initialVotingPower, 'voting power at vote should match vp without update') + }) }) context('voting as delegate', () => { @@ -965,6 +1030,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 await token.generateTokens(voters[i].account, voters[i].balance) await voting.assignDelegate(delegate1, {from: voters[i].account}) } + await token.generateTokens(ZERO_ADDRESS, bigExp(1, decimals)) executionTarget = await ExecutionTarget.new() @@ -985,6 +1051,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 await voting.vote(voteId, false, false, { from: holder29 }) await voting.attemptVoteForMultiple(voteId, false, [holder51], {from: delegate1}) + await assertRevert(voting.getVoterStateMultiple(voteId + 1, [holder51]), ERRORS.VOTING_NO_VOTE) const votersState = await voting.getVoterStateMultiple(voteId, [holder20, holder29, holder51]) assert.equal(votersState[0], VOTER_STATE.YEA, `holder20 should have 'yea' state`) assert.equal(votersState[1], VOTER_STATE.NAY, `holder29 should have 'nay' state`) @@ -1031,6 +1098,31 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 voting.attemptVoteForMultiple(voteId, true, [holder51, holder2, holder1], {from: delegate1}), ERRORS.VOTING_NO_VOTING_POWER ) + }) + + it(`skip zero address passed`, async () => { + // Skip if zero address is one of the voters + let tx = await voting.attemptVoteForMultiple(voteId, true, [holder51, ZERO_ADDRESS], {from: delegate1}) + + assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) + assertEvent(tx, 'CastVote', {expectedArgs: {voteId, voter: holder51, supports: true}}) + + // Revert if zero address is a delegate (can't delegate to zero address) + // This test was added to improve test coverage + await impersonateAccount(ZERO_ADDRESS) + const signerZero = await ethers.getSigner(ZERO_ADDRESS) + const signers = await ethers.getSigners(); + await signers[0].sendTransaction({ + to: signerZero.address, + value: ethers.utils.parseEther("1.0"), + }); + + // The revert is expected because the delegate is zero address, so it's + // impossible to delegate to it. But holder51 will be skipped. + await assertRevert( + voting.attemptVoteForMultiple(voteId, true, [holder51], {from: signerZero.address}), + ERRORS.VOTING_CAN_NOT_VOTE_FOR + ) }) it(`one of the voters voted beforehand`, async () => { diff --git a/apps/voting/test/helpers/errors.js b/apps/voting/test/helpers/errors.js index 1de6e6b1b..1fc006dc4 100644 --- a/apps/voting/test/helpers/errors.js +++ b/apps/voting/test/helpers/errors.js @@ -29,4 +29,7 @@ module.exports = makeErrorMappingProxy({ VOTING_SELF_DELEGATE: 'VOTING_SELF_DELEGATE', VOTING_DELEGATE_SAME_AS_PREV: 'VOTING_DELEGATE_SAME_AS_PREV', VOTING_DELEGATE_CANT_OVERWRITE: 'VOTING_DELEGATE_CANT_OVERWRITE', + VOTING_INVALID_LIMIT: 'VOTING_INVALID_LIMIT', + VOTING_INVALID_OFFSET: 'VOTING_INVALID_OFFSET', + VOTING_MAX_DELEGATED_VOTERS_REACHED: 'VOTING_MAX_DELEGATED_VOTERS_REACHED', }) diff --git a/apps/voting/test/voting.js b/apps/voting/test/voting.js index b31c6ec83..a0cb1b0a4 100644 --- a/apps/voting/test/voting.js +++ b/apps/voting/test/voting.js @@ -205,6 +205,24 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, n assert.equal(await voting.getVoterState(voteId, nonHolder), VOTER_STATE.ABSENT, 'nonHolder should not have voted') }) + it('fails creating a vote without CREATE_VOTES_ROLE permission', async () => { + await aclP.revokePermission(ANY_ENTITY, voting.address, CREATE_VOTES_ROLE, { from: root }) + await assertRevert(voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata', { from: holder29 }), ERRORS.APP_AUTH_FAILED) + await assertRevert(voting.methods['newVote(bytes,string,bool,bool)'](encodeCallScript([]), '', true, false, { from: holder29 }), ERRORS.APP_AUTH_FAILED) + + await aclP.grantPermission(holder51, voting.address, CREATE_VOTES_ROLE, { from: root }) + await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata', { from: holder51 }) + await voting.methods['newVote(bytes,string,bool,bool)'](encodeCallScript([]), '', true, false, { from: holder51 }) + }) + + it('fails trying to execute a non-existent vote', async () => { + await assertRevert(voting.executeVote(voteId + 1), ERRORS.VOTING_NO_VOTE) + }) + + it('fails getting a voter state for a non-existent vote', async () => { + await assertRevert(voting.getVoterState(voteId + 1, holder51), ERRORS.VOTING_NO_VOTE) + }) + it('fails getting a vote out of bounds', async () => { await assertRevert(voting.getVote(voteId + 1), ERRORS.VOTING_NO_VOTE) }) @@ -226,6 +244,14 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, n await voting.executeVote(voteId) // exec doesn't fail }) + it('fails changing required support without MODIFY_SUPPORT_ROLE permission', async () => { + await aclP.revokePermission(ANY_ENTITY, voting.address, MODIFY_SUPPORT_ROLE, { from: root }) + await assertRevert(voting.changeSupportRequiredPct(pct16(70), { from: holder29 }), ERRORS.APP_AUTH_FAILED) + + await aclP.grantPermission(holder51, voting.address, MODIFY_SUPPORT_ROLE, { from: root }) + await voting.changeSupportRequiredPct(pct16(70), { from: holder51 }) + }) + it('changing min quorum doesnt affect vote min quorum', async () => { await voting.changeMinAcceptQuorumPct(pct16(50)) @@ -245,6 +271,18 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, n await voting.executeVote(voteId) // exec doesn't fail }) + it('fails changing min quorum without MODIFY_QUORUM_ROLE permission', async () => { + await aclP.revokePermission(ANY_ENTITY, voting.address, MODIFY_QUORUM_ROLE, { from: root }) + await assertRevert(voting.changeMinAcceptQuorumPct(pct16(50), { from: holder29 }), ERRORS.APP_AUTH_FAILED) + + await aclP.grantPermission(holder51, voting.address, MODIFY_QUORUM_ROLE, { from: root }) + await voting.changeMinAcceptQuorumPct(pct16(50), { from: holder51 }) + }) + + it(`fails to vote for a non-existent vote`, async () => { + await assertRevert(voting.vote(voteId + 1, false, true, { from: holder29 }), ERRORS.VOTING_NO_VOTE) + }) + it('holder can vote', async () => { const tx = await voting.vote(voteId, false, true, { from: holder29 }) assertEvent(tx, 'CastVote', { expectedArgs: { voteId: voteId, voter: holder29, supports: false }}) @@ -297,6 +335,7 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, n }) it('canVote check works', async () => { + await assertRevert(voting.canVote(voteId + 1, holder29), ERRORS.VOTING_NO_VOTE) assert.equal(await voting.canVote(voteId, holder29), true, 'should be able to vote') await voting.mockIncreaseTime(mainPhase + 1) assert.equal(await voting.canVote(voteId, holder29), true, 'should be unable to vote') @@ -310,6 +349,7 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, n return (await voting.getVote(voteId))[phaseIndex] } + await assertRevert(voting.getVotePhase(voteId + 1), ERRORS.VOTING_NO_VOTE) const MAIN_PHASE = 0 assert.equal(await voting.getVotePhase(voteId), MAIN_PHASE, 'should be main phase') assert.equal(await extractPhaseFromGetVote(voteId), MAIN_PHASE, 'should be main phase') @@ -490,6 +530,8 @@ contract('Voting App', ([root, holder1, holder2, holder20, holder29, holder51, n // Account creating vote does not have any tokens and therefore doesn't vote const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) + await assertRevert(voting.canExecute(voteId + 1), ERRORS.VOTING_NO_VOTE) + assert.isFalse(await voting.canExecute(voteId), 'vote cannot be executed') await voting.vote(voteId, true, true, { from: holder1 }) From 3a0bbba47172aa4c855260b30b6b077797ebe46a Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 30 Apr 2024 15:33:34 +0200 Subject: [PATCH 068/100] refactor: imroved naming and minor optimization --- apps/voting/contracts/Voting.sol | 20 ++++++++++---------- apps/voting/test/delegation.js | 8 ++++---- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 362956734..5ab5057b5 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -64,7 +64,7 @@ contract Voting is IForwarder, AragonApp { mapping (address => VoterState) voters; } - struct DelegatedAddressList { + struct DelegatedVoters { address[] addresses; } @@ -83,10 +83,8 @@ contract Voting is IForwarder, AragonApp { uint256 public votesLength; uint64 public objectionPhaseTime; - // delegate -> [delegated voter address] - mapping(address => DelegatedAddressList) private delegatedVoters; - // voter -> delegate - mapping(address => Delegate) private delegates; + mapping(address /* delegate */ => DelegatedVoters) private delegatedVoters; + mapping(address /* voter */ => Delegate) private delegates; event StartVote(uint256 indexed voteId, address indexed creator, string metadata); event CastVote(uint256 indexed voteId, address indexed voter, bool supports, uint256 stake); @@ -96,7 +94,7 @@ contract Voting is IForwarder, AragonApp { event ChangeMinQuorum(uint64 minAcceptQuorumPct); event ChangeVoteTime(uint64 voteTime); event ChangeObjectionPhaseTime(uint64 objectionPhaseTime); - event AssignDelegate(address indexed voter, address indexed delegate); + event AssignDelegate(address indexed voter, address indexed assignedDelegate); event UnassignDelegate(address indexed voter, address indexed unassignedDelegate); event AttemptCastVoteAsDelegate(uint256 indexed voteId, address indexed delegate, address[] voters); @@ -226,6 +224,7 @@ contract Voting is IForwarder, AragonApp { function vote(uint256 _voteId, bool _supports, bool /* _executesIfDecided_deprecated */) external voteExists(_voteId) { Vote storage vote_ = votes[_voteId]; require(_isValidPhaseToVote(vote_, _supports), ERROR_CAN_NOT_VOTE); + // ↓ ↓ ↓ A legacy comment from the original Aragon codebase ↓ ↓ ↓ // This could re-enter, though we can assume the governance token is not malicious uint256 votingPower = token.balanceOfAt(msg.sender, vote_.snapshotBlock); require(votingPower > 0, ERROR_NO_VOTING_POWER); @@ -284,6 +283,7 @@ contract Voting is IForwarder, AragonApp { uint256 votingPower; for (uint256 i = 0; i < _voters.length; ++i) { voter = _voters[i]; + // ↓ ↓ ↓ A legacy comment from the original Aragon codebase ↓ ↓ ↓ // This could re-enter, though we can assume the governance token is not malicious votingPower = token.balanceOfAt(voter, vote_.snapshotBlock); @@ -517,8 +517,7 @@ contract Voting is IForwarder, AragonApp { * @return the array of governance token balances */ function getVotingPowerMultipleAtVote(uint256 _voteId, address[] _voters) external view voteExists(_voteId) returns (uint256[] memory balances) { - Vote storage vote_ = votes[_voteId]; - return _getVotingPowerMultipleAt(_voters, vote_.snapshotBlock); + return _getVotingPowerMultipleAt(_voters, votes[_voteId].snapshotBlock); } // Internal fns @@ -611,10 +610,11 @@ contract Voting is IForwarder, AragonApp { * @param _voter address of the voter */ function _addDelegatedAddressFor(address _delegate, address _voter) internal { - uint256 delegatedVotersCount = delegatedVoters[_delegate].addresses.length; + address[] storage votersList = delegatedVoters[_delegate].addresses; + uint256 delegatedVotersCount = votersList.length; require(delegatedVotersCount <= UINT_96_MAX, ERROR_MAX_DELEGATED_VOTERS_REACHED); - delegatedVoters[_delegate].addresses.push(_voter); + votersList.push(_voter); delegates[_voter] = Delegate(_delegate, uint96(delegatedVotersCount)); emit AssignDelegate(_voter, _delegate); } diff --git a/apps/voting/test/delegation.js b/apps/voting/test/delegation.js index ee522d3bd..0a88b10fc 100644 --- a/apps/voting/test/delegation.js +++ b/apps/voting/test/delegation.js @@ -71,7 +71,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const assignDelegate = async (delegate, holder ) => { const tx = await voting.assignDelegate(delegate, {from: holder}) assertEvent(tx, 'AssignDelegate', { - expectedArgs: {voter: holder, delegate} + expectedArgs: {voter: holder, assignedDelegate: delegate} }) assertAmountOfEvents(tx, 'AssignDelegate', {expectedAmount: 1}) } @@ -169,7 +169,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const tx = await voting.assignDelegate(delegate1, {from: holder29}) assertEvent(tx, 'AssignDelegate', { - expectedArgs: {voter: holder29, delegate: delegate1} + expectedArgs: {voter: holder29, assignedDelegate: delegate1} }) assertAmountOfEvents(tx, 'AssignDelegate', {expectedAmount: 1}) @@ -271,13 +271,13 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const tx1 = await voting.assignDelegate(delegate1, {from: holder29}) assertEvent(tx1, 'AssignDelegate', { - expectedArgs: {voter: holder29, delegate: delegate1} + expectedArgs: {voter: holder29, assignedDelegate: delegate1} }) assertAmountOfEvents(tx1, 'AssignDelegate', {expectedAmount: 1}) const tx2 = await voting.assignDelegate(delegate1, {from: holder51}) assertEvent(tx2, 'AssignDelegate', { - expectedArgs: {voter: holder51, delegate: delegate1} + expectedArgs: {voter: holder51, assignedDelegate: delegate1} }) assertAmountOfEvents(tx2, 'AssignDelegate', {expectedAmount: 1}) From c9b26ec6a47ae2b479704643a177127a930862b7 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 30 Apr 2024 15:46:21 +0200 Subject: [PATCH 069/100] refactor: check voter state in _vote earlier to optimize the gas --- apps/voting/contracts/Voting.sol | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 5ab5057b5..c5a40569d 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -559,10 +559,12 @@ contract Voting is IForwarder, AragonApp { VoterState state = vote_.voters[_voter]; // If voter had previously voted, decrease count - if (state == VoterState.Yea || state == VoterState.DelegateYea) { - vote_.yea = vote_.yea.sub(_votingPower); - } else if (state == VoterState.Nay || state == VoterState.DelegateNay) { - vote_.nay = vote_.nay.sub(_votingPower); + if (state != VoterState.Absent) { + if (state == VoterState.Yea || state == VoterState.DelegateYea) { + vote_.yea = vote_.yea.sub(_votingPower); + } else { // Remaining states are Nay and DelegateNay + vote_.nay = vote_.nay.sub(_votingPower); + } } if (_supports) { From 9b7af852faabfa48e32fdafe3d30455933fcdef4 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 30 Apr 2024 15:50:12 +0200 Subject: [PATCH 070/100] refactor: move local variables declaration to the loop since it doesnt affect gas cost --- apps/voting/contracts/Voting.sol | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index c5a40569d..9531d8f1c 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -279,13 +279,11 @@ contract Voting is IForwarder, AragonApp { require(_isValidPhaseToVote(vote_, _supports), ERROR_CAN_NOT_VOTE); bool votedForAtLeastOne = false; - address voter; - uint256 votingPower; for (uint256 i = 0; i < _voters.length; ++i) { - voter = _voters[i]; + address voter = _voters[i]; // ↓ ↓ ↓ A legacy comment from the original Aragon codebase ↓ ↓ ↓ // This could re-enter, though we can assume the governance token is not malicious - votingPower = token.balanceOfAt(voter, vote_.snapshotBlock); + uint256 votingPower = token.balanceOfAt(voter, vote_.snapshotBlock); // Voting power check is not used the same way as `_canVoteFor` to have consistency between `attemptVoteForMultiple` and `vote`. // Moreover, it's impossible to front-run a delegate by moving tokens before their voting attempt, From eeee19f1ea3df94895dadba2bc6b4eaf4873df0a Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 30 Apr 2024 16:17:04 +0200 Subject: [PATCH 071/100] refactor: remove excessive requires from getDelegatedVoters --- apps/voting/contracts/Voting.sol | 15 ++++++--------- apps/voting/test/delegation.js | 8 +++----- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 9531d8f1c..251d9247a 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -43,8 +43,6 @@ contract Voting is IForwarder, AragonApp { string private constant ERROR_DELEGATE_NOT_SET = "VOTING_DELEGATE_NOT_SET"; string private constant ERROR_SELF_DELEGATE = "VOTING_SELF_DELEGATE"; string private constant ERROR_DELEGATE_SAME_AS_PREV = "VOTING_DELEGATE_SAME_AS_PREV"; - string private constant ERROR_INVALID_LIMIT = "VOTING_INVALID_LIMIT"; - string private constant ERROR_INVALID_OFFSET = "VOTING_INVALID_OFFSET"; string private constant ERROR_MAX_DELEGATED_VOTERS_REACHED = "VOTING_MAX_DELEGATED_VOTERS_REACHED"; enum VoterState { Absent, Yea, Nay, DelegateYea, DelegateNay } @@ -448,18 +446,17 @@ contract Voting is IForwarder, AragonApp { */ function getDelegatedVoters(address _delegate, uint256 _offset, uint256 _limit) external view returns (address[] memory voters) { require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); - require(_limit > 0, ERROR_INVALID_LIMIT); - uint256 delegatedVotersCount = delegatedVoters[_delegate].addresses.length; - if (delegatedVotersCount == 0) { + + address[] storage votersList = delegatedVoters[_delegate].addresses; + uint256 votersCount = votersList.length; + if (_offset >= votersCount) { return voters; } - require(_offset < delegatedVotersCount, ERROR_INVALID_OFFSET); - uint256 returnCount = _offset.add(_limit) > delegatedVotersCount ? delegatedVotersCount.sub(_offset) : _limit; + uint256 returnCount = _offset.add(_limit) > votersCount ? votersCount.sub(_offset) : _limit; voters = new address[](returnCount); - address voter; for (uint256 i = 0; i < returnCount; ++i) { - voter = delegatedVoters[_delegate].addresses[_offset + i]; + address voter = votersList[_offset + i]; voters[i] = voter; } return voters; diff --git a/apps/voting/test/delegation.js b/apps/voting/test/delegation.js index 0a88b10fc..5df7e7e3f 100644 --- a/apps/voting/test/delegation.js +++ b/apps/voting/test/delegation.js @@ -906,11 +906,9 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 ) }) - it(`revert if "_offset" more or equal than delegated voters count`, async () => { - await assertRevert( - voting.getDelegatedVoters(delegate1, 10, 1), - ERRORS.VOTING_INVALID_OFFSET - ) + it(`if offset is more than length, return empty array`, async () => { + const delegatedVoters = await voting.getDelegatedVoters(nonHolder, 100, defaultLimit) + assert(delegatedVoters.length === 0, 'votersList should be empty') }) it(`if delegatedVoters array length is 0, return two empty arrays`, async () => { From f1f6de1606a6eda1999a0e1cc0ea895857101f88 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 7 May 2024 22:20:12 +0200 Subject: [PATCH 072/100] fix: additional comments and small tweaks after review --- apps/voting/contracts/Voting.sol | 25 ++++++++++++++++++------- apps/voting/test/delegation.js | 8 +++----- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 251d9247a..91f54a2de 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -222,7 +222,12 @@ contract Voting is IForwarder, AragonApp { function vote(uint256 _voteId, bool _supports, bool /* _executesIfDecided_deprecated */) external voteExists(_voteId) { Vote storage vote_ = votes[_voteId]; require(_isValidPhaseToVote(vote_, _supports), ERROR_CAN_NOT_VOTE); - // ↓ ↓ ↓ A legacy comment from the original Aragon codebase ↓ ↓ ↓ + /* + In version 0.4.24 of Solidity, for view methods, a regular CALL is used instead of STATICCALL. + This allows for the possibility of reentrance from the governance token. + However, we strongly assume here that the governance token is not malicious. + Below is a legacy comment from the original Aragon codebase. + */ // This could re-enter, though we can assume the governance token is not malicious uint256 votingPower = token.balanceOfAt(msg.sender, vote_.snapshotBlock); require(votingPower > 0, ERROR_NO_VOTING_POWER); @@ -279,7 +284,12 @@ contract Voting is IForwarder, AragonApp { bool votedForAtLeastOne = false; for (uint256 i = 0; i < _voters.length; ++i) { address voter = _voters[i]; - // ↓ ↓ ↓ A legacy comment from the original Aragon codebase ↓ ↓ ↓ + /* + In version 0.4.24 of Solidity, for view methods, a regular CALL is used instead of STATICCALL. + This allows for the possibility of reentrance from the governance token. + However, we strongly assume here that the governance token is not malicious. + Below is a legacy comment from the original Aragon codebase. + */ // This could re-enter, though we can assume the governance token is not malicious uint256 votingPower = token.balanceOfAt(voter, vote_.snapshotBlock); @@ -456,10 +466,8 @@ contract Voting is IForwarder, AragonApp { uint256 returnCount = _offset.add(_limit) > votersCount ? votersCount.sub(_offset) : _limit; voters = new address[](returnCount); for (uint256 i = 0; i < returnCount; ++i) { - address voter = votersList[_offset + i]; - voters[i] = voter; + voters[i] = votersList[_offset + i]; } - return voters; } /** @@ -553,7 +561,11 @@ contract Voting is IForwarder, AragonApp { Vote storage vote_ = votes[_voteId]; VoterState state = vote_.voters[_voter]; - // If voter had previously voted, decrease count + + /* + If voter had previously voted, decrease count. + Since the most common case is voter voting once, the additional check here works as a gas optimization. + */ if (state != VoterState.Absent) { if (state == VoterState.Yea || state == VoterState.DelegateYea) { vote_.yea = vote_.yea.sub(_votingPower); @@ -648,7 +660,6 @@ contract Voting is IForwarder, AragonApp { for (uint256 i = 0; i < votersCount; ++i) { balances[i] = token.balanceOfAt(_voters[i], _blockNumber); } - return balances; } /** diff --git a/apps/voting/test/delegation.js b/apps/voting/test/delegation.js index 5df7e7e3f..b6a7e3bdc 100644 --- a/apps/voting/test/delegation.js +++ b/apps/voting/test/delegation.js @@ -899,11 +899,9 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 ) }) - it(`revert if "_limit" is 0`, async () => { - await assertRevert( - voting.getDelegatedVoters(delegate1, 0, 0), - ERRORS.VOTING_INVALID_LIMIT - ) + it(`if "_limit" is 0, return empty array`, async () => { + const delegatedVoters = await voting.getDelegatedVoters(nonHolder, 0, 0) + assert(delegatedVoters.length === 0, 'votersList should be empty') }) it(`if offset is more than length, return empty array`, async () => { From e91d1a2413cc92118299b5b80095b2caefdaf93d Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 7 May 2024 22:22:55 +0200 Subject: [PATCH 073/100] refactor: add _votePhase arg to _vote fn --- apps/voting/contracts/Voting.sol | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 91f54a2de..18b6fd5ec 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -221,7 +221,10 @@ contract Voting is IForwarder, AragonApp { */ function vote(uint256 _voteId, bool _supports, bool /* _executesIfDecided_deprecated */) external voteExists(_voteId) { Vote storage vote_ = votes[_voteId]; - require(_isValidPhaseToVote(vote_, _supports), ERROR_CAN_NOT_VOTE); + require(_isVoteOpen(vote_), ERROR_CAN_NOT_VOTE); + VotePhase votePhase = _getVotePhase(vote_); + require(_isValidPhaseToVote(votePhase, _supports), ERROR_CAN_NOT_VOTE); + /* In version 0.4.24 of Solidity, for view methods, a regular CALL is used instead of STATICCALL. This allows for the possibility of reentrance from the governance token. @@ -231,7 +234,7 @@ contract Voting is IForwarder, AragonApp { // This could re-enter, though we can assume the governance token is not malicious uint256 votingPower = token.balanceOfAt(msg.sender, vote_.snapshotBlock); require(votingPower > 0, ERROR_NO_VOTING_POWER); - _vote(_voteId, _supports, msg.sender, false, votingPower); + _vote(_voteId, _supports, msg.sender, false, votingPower, votePhase); } /** @@ -279,7 +282,9 @@ contract Voting is IForwarder, AragonApp { */ function attemptVoteForMultiple(uint256 _voteId, bool _supports, address[] _voters) public voteExists(_voteId) { Vote storage vote_ = votes[_voteId]; - require(_isValidPhaseToVote(vote_, _supports), ERROR_CAN_NOT_VOTE); + require(_isVoteOpen(vote_), ERROR_CAN_NOT_VOTE); + VotePhase votePhase = _getVotePhase(vote_); + require(_isValidPhaseToVote(votePhase, _supports), ERROR_CAN_NOT_VOTE); bool votedForAtLeastOne = false; for (uint256 i = 0; i < _voters.length; ++i) { @@ -298,7 +303,7 @@ contract Voting is IForwarder, AragonApp { // but it is possible to front-run a delegate by unassigning them or voting before their voting attempt. require(votingPower > 0, ERROR_NO_VOTING_POWER); if (_canVoteFor(vote_, voter, msg.sender)) { - _vote(_voteId, _supports, voter, true, votingPower); + _vote(_voteId, _supports, voter, true, votingPower, votePhase); votedForAtLeastOne = true; } } @@ -557,7 +562,7 @@ contract Voting is IForwarder, AragonApp { * @param _voter The address of the voter * @param _isDelegate Whether the voter is a delegate */ - function _vote(uint256 _voteId, bool _supports, address _voter, bool _isDelegate, uint256 _votingPower) internal { + function _vote(uint256 _voteId, bool _supports, address _voter, bool _isDelegate, uint256 _votingPower, VotePhase _votePhase) internal { Vote storage vote_ = votes[_voteId]; VoterState state = vote_.voters[_voter]; @@ -584,7 +589,7 @@ contract Voting is IForwarder, AragonApp { emit CastVote(_voteId, _voter, _supports, _votingPower); - if (_getVotePhase(vote_) == VotePhase.Objection) { + if (_votePhase == VotePhase.Objection) { emit CastObjection(_voteId, _voter, _votingPower); } } @@ -696,12 +701,12 @@ contract Voting is IForwarder, AragonApp { /** * @dev Internal function to check if the vote is open and given option is applicable at the current phase. * It assumes the queried vote exists. - * @param vote_ The queried vote + * @param _votePhase The queried vote phase * @param _supports Whether the voter supports the vote * @return True if the given voter can participate a certain vote, false otherwise */ - function _isValidPhaseToVote(Vote storage vote_, bool _supports) internal view returns (bool) { - return _isVoteOpen(vote_) && (!_supports || _getVotePhase(vote_) == VotePhase.Main); + function _isValidPhaseToVote(VotePhase _votePhase, bool _supports) internal view returns (bool) { + return !_supports || _votePhase == VotePhase.Main; } /** From 86fd9f855f8a47a06e01630a240519fdc71f4afa Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Mon, 20 May 2024 14:30:13 +0200 Subject: [PATCH 074/100] docs: fix comments and natspec --- apps/voting/contracts/Voting.sol | 106 +++++++++++++++++-------------- 1 file changed, 59 insertions(+), 47 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 18b6fd5ec..d579fc297 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -200,7 +200,7 @@ contract Voting is IForwarder, AragonApp { * @param _metadata Vote metadata * @dev _castVote_deprecated Whether to also cast newly created vote - DEPRECATED * @dev _executesIfDecided_deprecated Whether to also immediately execute newly created vote if decided - DEPRECATED - * @return voteId id for newly created vote + * @return voteId Id for newly created vote */ function newVote(bytes _executionScript, string _metadata, bool /* _castVote_deprecated */, bool /* _executesIfDecided_deprecated */) external @@ -215,7 +215,7 @@ contract Voting is IForwarder, AragonApp { * @dev Initialization check is implicitly provided by `voteExists()` as new votes can only be * created via `newVote(),` which requires initialization * @dev _executesIfDecided was deprecated to introduce a proper lock period between decision and execution. - * @param _voteId Id for vote + * @param _voteId Vote identifier * @param _supports Whether voter supports the vote * @dev _executesIfDecided_deprecated Whether the vote should execute its action if it becomes decided - DEPRECATED */ @@ -241,15 +241,15 @@ contract Voting is IForwarder, AragonApp { * @notice Execute vote #`_voteId` * @dev Initialization check is implicitly provided by `voteExists()` as new votes can only be * created via `newVote(),` which requires initialization - * @param _voteId Id for vote + * @param _voteId Vote identifier */ function executeVote(uint256 _voteId) external voteExists(_voteId) { _executeVote(_voteId); } /** - * @notice Assign `_delegate` as the delegate for the sender - * @param _delegate address to delegate to + * @notice Assign `_delegate` as the sender's delegate, allowing `_delegate` to vote on behalf of the sender + * @param _delegate Address of the account to delegate to */ function assignDelegate(address _delegate) external { require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); @@ -265,7 +265,7 @@ contract Voting is IForwarder, AragonApp { } /** - * @notice Unassign `_delegate` from the sender + * @notice Unassign `_delegate` from the sender and remove the sender from the `_delegate's` list of delegated addresses */ function unassignDelegate() external { address prevDelegate = delegates[msg.sender].delegate; @@ -275,10 +275,11 @@ contract Voting is IForwarder, AragonApp { } /** - * @notice Vote `_supports ? 'yes' : 'no'` in vote #`_voteId` on behalf of the `_voters` list - * @param _voteId Id for vote + * @notice Vote `_supports ? 'yes' : 'no'` in vote #`_voteId` on behalf of the `_voters` list. + * Each voter from the list must have assigned the sender as their delegate with `assignDelegate`. + * @param _voteId Vote identifier * @param _supports Whether the delegate supports the vote - * @param _voters list of voters + * @param _voters List of voters */ function attemptVoteForMultiple(uint256 _voteId, bool _supports, address[] _voters) public voteExists(_voteId) { Vote storage vote_ = votes[_voteId]; @@ -298,10 +299,9 @@ contract Voting is IForwarder, AragonApp { // This could re-enter, though we can assume the governance token is not malicious uint256 votingPower = token.balanceOfAt(voter, vote_.snapshotBlock); - // Voting power check is not used the same way as `_canVoteFor` to have consistency between `attemptVoteForMultiple` and `vote`. - // Moreover, it's impossible to front-run a delegate by moving tokens before their voting attempt, - // but it is possible to front-run a delegate by unassigning them or voting before their voting attempt. + // A strict check for balance was introduced in order to achieve the same logic as in the `vote` fn require(votingPower > 0, ERROR_NO_VOTING_POWER); + // However, this check is not strict, because otherwise it could lead to a front-run attack, ruining the whole attempt if (_canVoteFor(vote_, voter, msg.sender)) { _vote(_voteId, _supports, voter, true, votingPower, votePhase); votedForAtLeastOne = true; @@ -313,10 +313,11 @@ contract Voting is IForwarder, AragonApp { } /** - * @notice Vote `_supports ? 'yes' : 'no'` in vote #`_voteId` on behalf of the `_voter` - * @param _voteId Id for vote + * @notice Vote `_supports ? 'yes' : 'no'` in vote #`_voteId` on behalf of the `_voter`. + * The `_voter` must have assigned the sender as their delegate with `assignDelegate`. + * @param _voteId Vote identifier * @param _supports Whether the delegate supports the vote - * @param _voter address of the voter + * @param _voter Address of the voter */ function attemptVoteFor(uint256 _voteId, bool _supports, address _voter) external { address[] memory voters = new address[](1); @@ -324,7 +325,9 @@ contract Voting is IForwarder, AragonApp { attemptVoteForMultiple(_voteId, _supports, voters); } - // Forwarding fns + // --- + // FORWARDING METHODS + // --- /** * @notice Tells whether the Voting app is a forwarder @@ -356,7 +359,9 @@ contract Voting is IForwarder, AragonApp { return canPerform(_sender, CREATE_VOTES_ROLE, arr()); } - // Getter fns + // --- + // GETTER METHODS + // --- /** * @notice Tells whether a vote #`_voteId` can be executed @@ -374,7 +379,7 @@ contract Voting is IForwarder, AragonApp { * @dev Initialization check is implicitly provided by `voteExists()` as new votes can only be * created via `newVote(),` which requires initialization * @param _voteId Vote identifier - * @param _voter address of the voter to check + * @param _voter Address of the voter * @return True if the given voter can participate in the main phase of a certain vote, false otherwise */ function canVote(uint256 _voteId, address _voter) external view voteExists(_voteId) returns (bool) { @@ -445,7 +450,7 @@ contract Voting is IForwarder, AragonApp { /** * @dev Return the state of a voter for a given vote by its ID * @param _voteId Vote identifier - * @param _voter address of the voter + * @param _voter Address of the voter * @return VoterState of the requested voter for a certain vote */ function getVoterState(uint256 _voteId, address _voter) external view voteExists(_voteId) returns (VoterState) { @@ -454,10 +459,10 @@ contract Voting is IForwarder, AragonApp { /** * @notice Return the sliced list of delegated voters for `_delegate` - * @param _delegate the address of the delegate - * @param _offset the number of delegated voters from the start of the list to skip - * @param _limit the number of delegated voters to return - * @return the array of delegated voters + * @param _delegate Address of the delegate + * @param _offset Number of delegated voters from the start of the list to skip + * @param _limit Number of delegated voters to return + * @return Array of delegated voters' addresses */ function getDelegatedVoters(address _delegate, uint256 _offset, uint256 _limit) external view returns (address[] memory voters) { require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); @@ -477,8 +482,8 @@ contract Voting is IForwarder, AragonApp { /** * @dev Return the number of delegated voters for the `_delegate` - * @param _delegate address of the delegate - * @return the number of delegated voters + * @param _delegate Address of the delegate + * @return Number of `_delegate`'s delegated voters */ function getDelegatedVotersCount(address _delegate) external view returns (uint256) { require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); @@ -487,7 +492,7 @@ contract Voting is IForwarder, AragonApp { /** * @notice Return the delegate address assigned to the `_voter` - * @param _voter the address of the voter + * @param _voter Address of the voter */ function getDelegate(address _voter) external view returns (address) { require(_voter != address(0), ERROR_ZERO_ADDRESS_PASSED); @@ -497,8 +502,8 @@ contract Voting is IForwarder, AragonApp { /** * @notice Return the list of `VoterState` for the `_voters` for the vote #`_voteId` * @param _voteId Vote identifier - * @param _voters list of voters - * @return the array of voter states + * @param _voters List of voters + * @return Array of voter states */ function getVoterStateMultiple(uint256 _voteId, address[] _voters) external view voteExists(_voteId) returns (VoterState[] memory voterStatesList) { uint256 votersCount = _voters.length; @@ -511,8 +516,8 @@ contract Voting is IForwarder, AragonApp { /** * @notice Return the voting power of the `_voters` at the current block - * @param _voters list of voters - * @return the array of governance token balances + * @param _voters List of voters + * @return Array of governance token balances */ function getVotingPowerMultiple(address[] _voters) external view returns (uint256[] memory balances) { return _getVotingPowerMultipleAt(_voters, getBlockNumber64()); @@ -521,14 +526,16 @@ contract Voting is IForwarder, AragonApp { /** * @notice Return the voting power of the `_voters` at the vote #`_voteId` snapshot block * @param _voteId Vote identifier - * @param _voters list of voters - * @return the array of governance token balances + * @param _voters List of voters + * @return Array of governance token balances */ function getVotingPowerMultipleAtVote(uint256 _voteId, address[] _voters) external view voteExists(_voteId) returns (uint256[] memory balances) { return _getVotingPowerMultipleAt(_voters, votes[_voteId].snapshotBlock); } - // Internal fns + // --- + // INTERNAL METHODS + // --- /** * @dev Internal function to create a new vote @@ -557,9 +564,9 @@ contract Voting is IForwarder, AragonApp { /** * @dev Internal function to cast a vote or object to. * @dev It assumes that voter can support or object to the vote - * @param _voteId The identifier of the vote + * @param _voteId Vote identifier * @param _supports Whether the voter supports the vote - * @param _voter The address of the voter + * @param _voter Address of the voter * @param _isDelegate Whether the voter is a delegate */ function _vote(uint256 _voteId, bool _supports, address _voter, bool _isDelegate, uint256 _votingPower, VotePhase _votePhase) internal { @@ -596,7 +603,7 @@ contract Voting is IForwarder, AragonApp { /** * @dev Internal function to execute a vote. It assumes the queried vote exists. - * @param _voteId The identifier of the vote + * @param _voteId Vote identifier */ function _executeVote(uint256 _voteId) internal { require(_canExecute(_voteId), ERROR_CAN_NOT_EXECUTE); @@ -605,7 +612,7 @@ contract Voting is IForwarder, AragonApp { /** * @dev Unsafe version of _executeVote that assumes you have already checked if the vote can be executed and exists - * @param _voteId The identifier of the vote + * @param _voteId Vote identifier */ function _unsafeExecuteVote(uint256 _voteId) internal { Vote storage vote_ = votes[_voteId]; @@ -620,8 +627,8 @@ contract Voting is IForwarder, AragonApp { /** * @dev internal function to add the `_voter` to the list of delegated voters for the `_delegate` and update the delegate for the `_voter` - * @param _delegate address of the delegate - * @param _voter address of the voter + * @param _delegate Address of the delegate + * @param _voter Address of the voter */ function _addDelegatedAddressFor(address _delegate, address _voter) internal { address[] storage votersList = delegatedVoters[_delegate].addresses; @@ -635,8 +642,8 @@ contract Voting is IForwarder, AragonApp { /** * @dev internal function to remove the `_voter` from the list of delegated voters for the `_delegate` - * @param _delegate address of the delegate - * @param _voter address of the voter + * @param _delegate Address of the delegate + * @param _voter Address of the voter */ function _removeDelegatedAddressFor(address _delegate, address _voter) internal { uint96 voterIndex = delegates[_voter].voterIndex; @@ -655,9 +662,9 @@ contract Voting is IForwarder, AragonApp { } /** - * @dev internal function to get the voting power of the `_voters` at the `_blockNumber` - * @param _voters list of voters - * @param _blockNumber target block number + * @dev Internal function to get the voting power of the `_voters` at the `_blockNumber` + * @param _voters List of voters + * @param _blockNumber Target block number */ function _getVotingPowerMultipleAt(address[] _voters, uint256 _blockNumber) internal view returns (uint256[] memory balances) { uint256 votersCount = _voters.length; @@ -669,7 +676,7 @@ contract Voting is IForwarder, AragonApp { /** * @dev Internal function to check if a vote can be executed. It assumes the queried vote exists. - * @param _voteId The identifier of the vote + * @param _voteId Vote identifier * @return True if the given vote can be executed, false otherwise */ function _canExecute(uint256 _voteId) internal view returns (bool) { @@ -712,8 +719,8 @@ contract Voting is IForwarder, AragonApp { /** * @dev Internal function to check if the _delegate can vote on behalf of the _voter in the given vote * @param vote_ The queried vote - * @param _delegate address of the delegate - * @param _voter address of the voter + * @param _delegate Address of the delegate + * @param _voter Address of the voter * @return True if the _delegate can vote on behalf of the _voter in the given vote, false otherwise */ function _canVoteFor(Vote storage vote_, address _voter, address _delegate) internal view returns (bool) { @@ -753,6 +760,11 @@ contract Voting is IForwarder, AragonApp { * @return True if less than voteTime has passed since the vote start */ function _isVoteOpen(Vote storage vote_) internal view returns (bool) { + /* + The `!vote_.executed` check must stay in place. + In a particular case `unsafelyChangeVoteTime` call might + break the vote state and make it possible to vote for the executed vote. + */ return getTimestamp64() < vote_.startDate.add(voteTime) && !vote_.executed; } From 38d56a2d1b97dda3489decd43063ef5e0d320916 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Wed, 22 May 2024 14:21:44 +0200 Subject: [PATCH 075/100] docs: update comments --- apps/voting/contracts/Voting.sol | 37 ++++++++++++++++---------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index d579fc297..c5d7c21db 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -94,6 +94,10 @@ contract Voting is IForwarder, AragonApp { event ChangeObjectionPhaseTime(uint64 objectionPhaseTime); event AssignDelegate(address indexed voter, address indexed assignedDelegate); event UnassignDelegate(address indexed voter, address indexed unassignedDelegate); + + // IMPORTANT: The voters array contains ALL voters the vote was attempted. + // It's not possible to distinguish successful and skipped votes based only on this event. + // See the `attemptVoteForMultiple` method for details. event AttemptCastVoteAsDelegate(uint256 indexed voteId, address indexed delegate, address[] voters); modifier voteExists(uint256 _voteId) { @@ -225,13 +229,9 @@ contract Voting is IForwarder, AragonApp { VotePhase votePhase = _getVotePhase(vote_); require(_isValidPhaseToVote(votePhase, _supports), ERROR_CAN_NOT_VOTE); - /* - In version 0.4.24 of Solidity, for view methods, a regular CALL is used instead of STATICCALL. - This allows for the possibility of reentrance from the governance token. - However, we strongly assume here that the governance token is not malicious. - Below is a legacy comment from the original Aragon codebase. - */ - // This could re-enter, though we can assume the governance token is not malicious + // In version 0.4.24 of Solidity, for view methods, a regular CALL is used instead of STATICCALL. + // This allows for the possibility of reentrance from the governance token. + // However, we strongly assume here that the governance token is not malicious. uint256 votingPower = token.balanceOfAt(msg.sender, vote_.snapshotBlock); require(votingPower > 0, ERROR_NO_VOTING_POWER); _vote(_voteId, _supports, msg.sender, false, votingPower, votePhase); @@ -290,16 +290,14 @@ contract Voting is IForwarder, AragonApp { bool votedForAtLeastOne = false; for (uint256 i = 0; i < _voters.length; ++i) { address voter = _voters[i]; - /* - In version 0.4.24 of Solidity, for view methods, a regular CALL is used instead of STATICCALL. - This allows for the possibility of reentrance from the governance token. - However, we strongly assume here that the governance token is not malicious. - Below is a legacy comment from the original Aragon codebase. - */ - // This could re-enter, though we can assume the governance token is not malicious + + // In version 0.4.24 of Solidity, for view methods, a regular CALL is used instead of STATICCALL. + // This allows for the possibility of reentrance from the governance token. + // However, we strongly assume here that the governance token is not malicious. uint256 votingPower = token.balanceOfAt(voter, vote_.snapshotBlock); - // A strict check for balance was introduced in order to achieve the same logic as in the `vote` fn + // A strict check for balance was introduced to have a persistent logic with the `vote` method. + // It's not possible to front-run the voting attempt with balance manipulation since the balances are being checked at the vote's snapshot block require(votingPower > 0, ERROR_NO_VOTING_POWER); // However, this check is not strict, because otherwise it could lead to a front-run attack, ruining the whole attempt if (_canVoteFor(vote_, voter, msg.sender)) { @@ -309,6 +307,9 @@ contract Voting is IForwarder, AragonApp { } require(votedForAtLeastOne, ERROR_CAN_NOT_VOTE_FOR); + // IMPORTANT: The voters array contains ALL voters the vote was attempted. + // It's not possible to distinguish successful and skipped votes based only on this event. + // To filter votes, use this event along with the `CastVote` event, which is emitted for each successful voting attempt. emit AttemptCastVoteAsDelegate(_voteId, msg.sender, _voters); } @@ -574,10 +575,8 @@ contract Voting is IForwarder, AragonApp { VoterState state = vote_.voters[_voter]; - /* - If voter had previously voted, decrease count. - Since the most common case is voter voting once, the additional check here works as a gas optimization. - */ + // If voter had previously voted, decrease count. + // Since the most common case is voter voting once, the additional check here works as a gas optimization. if (state != VoterState.Absent) { if (state == VoterState.Yea || state == VoterState.DelegateYea) { vote_.yea = vote_.yea.sub(_votingPower); From 6b12964a040fa2098b544a0875bba7e81deed192 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Fri, 24 May 2024 13:03:42 +0200 Subject: [PATCH 076/100] refactor: after-review fixes --- apps/voting/contracts/Voting.sol | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index c5d7c21db..30cf7ade4 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -234,7 +234,7 @@ contract Voting is IForwarder, AragonApp { // However, we strongly assume here that the governance token is not malicious. uint256 votingPower = token.balanceOfAt(msg.sender, vote_.snapshotBlock); require(votingPower > 0, ERROR_NO_VOTING_POWER); - _vote(_voteId, _supports, msg.sender, false, votingPower, votePhase); + _vote(_voteId, votePhase, /* voter */ msg.sender, _supports, votingPower, /* isDelegate */ false); } /** @@ -299,9 +299,11 @@ contract Voting is IForwarder, AragonApp { // A strict check for balance was introduced to have a persistent logic with the `vote` method. // It's not possible to front-run the voting attempt with balance manipulation since the balances are being checked at the vote's snapshot block require(votingPower > 0, ERROR_NO_VOTING_POWER); - // However, this check is not strict, because otherwise it could lead to a front-run attack, ruining the whole attempt + // The _canVoteFor() check below does not revert to avoid a situation where a voter votes by themselves + // or undelegates previously delegated voting power before the delegate sends a transaction. This approach + // helps protect the entire transaction from failing due to the actions of a single misbehaving voter. if (_canVoteFor(vote_, voter, msg.sender)) { - _vote(_voteId, _supports, voter, true, votingPower, votePhase); + _vote(_voteId, votePhase, voter, _supports, votingPower, /* isDelegate */ true); votedForAtLeastOne = true; } } @@ -570,11 +572,10 @@ contract Voting is IForwarder, AragonApp { * @param _voter Address of the voter * @param _isDelegate Whether the voter is a delegate */ - function _vote(uint256 _voteId, bool _supports, address _voter, bool _isDelegate, uint256 _votingPower, VotePhase _votePhase) internal { + function _vote(uint256 _voteId, VotePhase _votePhase, address _voter, bool _supports, uint256 _votingPower, bool _isDelegate) internal { Vote storage vote_ = votes[_voteId]; VoterState state = vote_.voters[_voter]; - // If voter had previously voted, decrease count. // Since the most common case is voter voting once, the additional check here works as a gas optimization. if (state != VoterState.Absent) { @@ -711,7 +712,7 @@ contract Voting is IForwarder, AragonApp { * @param _supports Whether the voter supports the vote * @return True if the given voter can participate a certain vote, false otherwise */ - function _isValidPhaseToVote(VotePhase _votePhase, bool _supports) internal view returns (bool) { + function _isValidPhaseToVote(VotePhase _votePhase, bool _supports) internal pure returns (bool) { return !_supports || _votePhase == VotePhase.Main; } From 070e8f378379de33394d70e83f476370904203c0 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 18 Jun 2024 10:56:19 +0200 Subject: [PATCH 077/100] docs: fix comment style --- apps/voting/contracts/Voting.sol | 8 +- yarn.lock | 1448 +++++++++++++++++++++++------- 2 files changed, 1133 insertions(+), 323 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 30cf7ade4..423528f22 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -760,11 +760,9 @@ contract Voting is IForwarder, AragonApp { * @return True if less than voteTime has passed since the vote start */ function _isVoteOpen(Vote storage vote_) internal view returns (bool) { - /* - The `!vote_.executed` check must stay in place. - In a particular case `unsafelyChangeVoteTime` call might - break the vote state and make it possible to vote for the executed vote. - */ + // The `!vote_.executed` check must stay in place. + // In a particular case `unsafelyChangeVoteTime` call might + // break the vote state and make it possible to vote for the executed vote. return getTimestamp64() < vote_.startDate.add(voteTime) && !vote_.executed; } diff --git a/yarn.lock b/yarn.lock index 4344937ca..8af9fda56 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@adraffy/ens-normalize@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz#d2a39395c587e092d77cbbc80acf956a54f38bf7" + integrity sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q== + "@aragon/contract-helpers-test@0.1.0", "@aragon/contract-helpers-test@^0.1.0": version "0.1.0" resolved "https://registry.yarnpkg.com/@aragon/contract-helpers-test/-/contract-helpers-test-0.1.0.tgz#5c5f09739a0b33ab66843bc4849cb2b997d88af0" @@ -221,30 +226,10 @@ "@babel/helper-validator-identifier" "^7.14.9" to-fast-properties "^2.0.0" -"@ethereumjs/block@^3.4.0", "@ethereumjs/block@^3.5.0", "@ethereumjs/block@^3.5.1": - version "3.5.1" - resolved "https://registry.yarnpkg.com/@ethereumjs/block/-/block-3.5.1.tgz#59737d393503249aa750c37dfc83896234f4e175" - integrity sha512-MoY9bHKABOBK6BW0v1N1Oc0Cve4x/giX67M3TtrVBUsKQTj2eznLGKpydoitxWSZ+WgKKSVhfRMzbCGRwk7T5w== - dependencies: - "@ethereumjs/common" "^2.5.0" - "@ethereumjs/tx" "^3.3.1" - ethereumjs-util "^7.1.1" - merkle-patricia-tree "^4.2.1" - -"@ethereumjs/blockchain@^5.4.0", "@ethereumjs/blockchain@^5.4.1": - version "5.4.2" - resolved "https://registry.yarnpkg.com/@ethereumjs/blockchain/-/blockchain-5.4.2.tgz#5074e0a0157818762a5f5175ea0bd93c5455fe32" - integrity sha512-AOAAwz/lw2lciG9gf5wHi7M/qknraXXnLR66lYgbQ04qfyFC3ZE5x/5rLVm1Vu+kfJLlKrYZTmA0IbOkc7kvgw== - dependencies: - "@ethereumjs/block" "^3.5.1" - "@ethereumjs/common" "^2.5.0" - "@ethereumjs/ethash" "^1.1.0" - debug "^2.2.0" - ethereumjs-util "^7.1.1" - level-mem "^5.0.1" - lru-cache "^5.1.1" - rlp "^2.2.4" - semaphore-async-await "^1.5.1" +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== "@ethereumjs/common@^2.3.0", "@ethereumjs/common@^2.4.0", "@ethereumjs/common@^2.5.0": version "2.5.0" @@ -262,18 +247,12 @@ crc-32 "^1.2.0" ethereumjs-util "^7.1.4" -"@ethereumjs/ethash@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@ethereumjs/ethash/-/ethash-1.1.0.tgz#7c5918ffcaa9cb9c1dc7d12f77ef038c11fb83fb" - integrity sha512-/U7UOKW6BzpA+Vt+kISAoeDie1vAvY4Zy2KF5JJb+So7+1yKmJeJEHOGSnQIj330e9Zyl3L5Nae6VZyh2TJnAA== - dependencies: - "@ethereumjs/block" "^3.5.0" - "@types/levelup" "^4.3.0" - buffer-xor "^2.0.1" - ethereumjs-util "^7.1.1" - miller-rabin "^4.0.0" +"@ethereumjs/rlp@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@ethereumjs/rlp/-/rlp-4.0.1.tgz#626fabfd9081baab3d0a3074b0c7ecaf674aaa41" + integrity sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw== -"@ethereumjs/tx@^3.2.1", "@ethereumjs/tx@^3.3.0", "@ethereumjs/tx@^3.3.1": +"@ethereumjs/tx@^3.2.1", "@ethereumjs/tx@^3.3.0": version "3.3.2" resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-3.3.2.tgz#348d4624bf248aaab6c44fec2ae67265efe3db00" integrity sha512-6AaJhwg4ucmwTvw/1qLaZUX5miWrwZ4nLOUsKyb/HtzS3BMw/CasKhdi1ims9mBKeK9sOJCH4qGKOBGyJCeeog== @@ -289,24 +268,14 @@ "@ethereumjs/common" "^2.6.3" ethereumjs-util "^7.1.4" -"@ethereumjs/vm@^5.5.2": - version "5.5.3" - resolved "https://registry.yarnpkg.com/@ethereumjs/vm/-/vm-5.5.3.tgz#dc8b30dd35efb589db093592600207660fa8dada" - integrity sha512-0k5OreWnlgXYs54wohgO11jtGI05GDasj2EYxzuaStxTi15CS3vow5wGYELC1pG9xngE1F/mFmKi/f14XRuDow== +"@ethereumjs/util@^8.1.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/util/-/util-8.1.0.tgz#299df97fb6b034e0577ce9f94c7d9d1004409ed4" + integrity sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA== dependencies: - "@ethereumjs/block" "^3.5.0" - "@ethereumjs/blockchain" "^5.4.1" - "@ethereumjs/common" "^2.5.0" - "@ethereumjs/tx" "^3.3.1" - async-eventemitter "^0.2.4" - core-js-pure "^3.0.1" - debug "^2.2.0" - ethereumjs-util "^7.1.1" - functional-red-black-tree "^1.0.1" - mcl-wasm "^0.7.1" - merkle-patricia-tree "^4.2.1" - rustbn.js "~0.2.0" - util.promisify "^1.0.1" + "@ethereumjs/rlp" "^4.0.1" + ethereum-cryptography "^2.0.0" + micro-ftch "^0.3.1" "@ethersproject/abi@5.0.0-beta.153": version "5.0.0-beta.153" @@ -353,6 +322,21 @@ "@ethersproject/properties" "^5.5.0" "@ethersproject/strings" "^5.5.0" +"@ethersproject/abi@^5.0.9", "@ethersproject/abi@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" + integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== + dependencies: + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + "@ethersproject/abstract-provider@5.5.1", "@ethersproject/abstract-provider@^5.5.0": version "5.5.1" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.5.1.tgz#2f1f6e8a3ab7d378d8ad0b5718460f85649710c5" @@ -366,6 +350,19 @@ "@ethersproject/transactions" "^5.5.0" "@ethersproject/web" "^5.5.0" +"@ethersproject/abstract-provider@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz#b0a8550f88b6bf9d51f90e4795d48294630cb9ef" + integrity sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/networks" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/web" "^5.7.0" + "@ethersproject/abstract-signer@5.5.0", "@ethersproject/abstract-signer@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.5.0.tgz#590ff6693370c60ae376bf1c7ada59eb2a8dd08d" @@ -377,6 +374,17 @@ "@ethersproject/logger" "^5.5.0" "@ethersproject/properties" "^5.5.0" +"@ethersproject/abstract-signer@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz#13f4f32117868452191a4649723cb086d2b596b2" + integrity sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ== + dependencies: + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/address@5.5.0", "@ethersproject/address@>=5.0.0-beta.128", "@ethersproject/address@^5.0.2", "@ethersproject/address@^5.0.4", "@ethersproject/address@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.5.0.tgz#bcc6f576a553f21f3dd7ba17248f81b473c9c78f" @@ -388,6 +396,17 @@ "@ethersproject/logger" "^5.5.0" "@ethersproject/rlp" "^5.5.0" +"@ethersproject/address@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37" + integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + "@ethersproject/base64@5.5.0", "@ethersproject/base64@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.5.0.tgz#881e8544e47ed976930836986e5eb8fab259c090" @@ -395,6 +414,13 @@ dependencies: "@ethersproject/bytes" "^5.5.0" +"@ethersproject/base64@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.7.0.tgz#ac4ee92aa36c1628173e221d0d01f53692059e1c" + integrity sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/basex@5.5.0", "@ethersproject/basex@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.5.0.tgz#e40a53ae6d6b09ab4d977bd037010d4bed21b4d3" @@ -412,6 +438,15 @@ "@ethersproject/logger" "^5.5.0" bn.js "^4.11.9" +"@ethersproject/bignumber@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.7.0.tgz#e2f03837f268ba655ffba03a57853e18a18dc9c2" + integrity sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + bn.js "^5.2.1" + "@ethersproject/bytes@5.5.0", "@ethersproject/bytes@>=5.0.0-beta.129", "@ethersproject/bytes@^5.0.4", "@ethersproject/bytes@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.5.0.tgz#cb11c526de657e7b45d2e0f0246fb3b9d29a601c" @@ -419,6 +454,13 @@ dependencies: "@ethersproject/logger" "^5.5.0" +"@ethersproject/bytes@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d" + integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A== + dependencies: + "@ethersproject/logger" "^5.7.0" + "@ethersproject/constants@5.5.0", "@ethersproject/constants@>=5.0.0-beta.128", "@ethersproject/constants@^5.0.4", "@ethersproject/constants@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.5.0.tgz#d2a2cd7d94bd1d58377d1d66c4f53c9be4d0a45e" @@ -426,6 +468,13 @@ dependencies: "@ethersproject/bignumber" "^5.5.0" +"@ethersproject/constants@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.7.0.tgz#df80a9705a7e08984161f09014ea012d1c75295e" + integrity sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/contracts@5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.5.0.tgz#b735260d4bd61283a670a82d5275e2a38892c197" @@ -456,6 +505,21 @@ "@ethersproject/properties" "^5.5.0" "@ethersproject/strings" "^5.5.0" +"@ethersproject/hash@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7" + integrity sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g== + dependencies: + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + "@ethersproject/hdnode@5.5.0", "@ethersproject/hdnode@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.5.0.tgz#4a04e28f41c546f7c978528ea1575206a200ddf6" @@ -501,11 +565,24 @@ "@ethersproject/bytes" "^5.5.0" js-sha3 "0.8.0" +"@ethersproject/keccak256@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.7.0.tgz#3186350c6e1cd6aba7940384ec7d6d9db01f335a" + integrity sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + js-sha3 "0.8.0" + "@ethersproject/logger@5.5.0", "@ethersproject/logger@>=5.0.0-beta.129", "@ethersproject/logger@^5.0.5", "@ethersproject/logger@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.5.0.tgz#0c2caebeff98e10aefa5aef27d7441c7fd18cf5d" integrity sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg== +"@ethersproject/logger@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892" + integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig== + "@ethersproject/networks@5.5.0", "@ethersproject/networks@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.5.0.tgz#babec47cab892c51f8dd652ce7f2e3e14283981a" @@ -513,6 +590,13 @@ dependencies: "@ethersproject/logger" "^5.5.0" +"@ethersproject/networks@^5.7.0": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.7.1.tgz#118e1a981d757d45ccea6bb58d9fd3d9db14ead6" + integrity sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ== + dependencies: + "@ethersproject/logger" "^5.7.0" + "@ethersproject/pbkdf2@5.5.0", "@ethersproject/pbkdf2@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.5.0.tgz#e25032cdf02f31505d47afbf9c3e000d95c4a050" @@ -528,6 +612,13 @@ dependencies: "@ethersproject/logger" "^5.5.0" +"@ethersproject/properties@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.7.0.tgz#a6e12cb0439b878aaf470f1902a176033067ed30" + integrity sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw== + dependencies: + "@ethersproject/logger" "^5.7.0" + "@ethersproject/providers@5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.5.0.tgz#bc2876a8fe5e0053ed9828b1f3767ae46e43758b" @@ -569,6 +660,14 @@ "@ethersproject/bytes" "^5.5.0" "@ethersproject/logger" "^5.5.0" +"@ethersproject/rlp@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.7.0.tgz#de39e4d5918b9d74d46de93af80b7685a9c21304" + integrity sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/sha2@5.5.0", "@ethersproject/sha2@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.5.0.tgz#a40a054c61f98fd9eee99af2c3cc6ff57ec24db7" @@ -590,6 +689,18 @@ elliptic "6.5.4" hash.js "1.1.7" +"@ethersproject/signing-key@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.7.0.tgz#06b2df39411b00bc57c7c09b01d1e41cf1b16ab3" + integrity sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + bn.js "^5.2.1" + elliptic "6.5.4" + hash.js "1.1.7" + "@ethersproject/solidity@5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.5.0.tgz#2662eb3e5da471b85a20531e420054278362f93f" @@ -611,6 +722,15 @@ "@ethersproject/constants" "^5.5.0" "@ethersproject/logger" "^5.5.0" +"@ethersproject/strings@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.7.0.tgz#54c9d2a7c57ae8f1205c88a9d3a56471e14d5ed2" + integrity sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/transactions@5.5.0", "@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.5.0.tgz#7e9bf72e97bcdf69db34fe0d59e2f4203c7a2908" @@ -626,6 +746,21 @@ "@ethersproject/rlp" "^5.5.0" "@ethersproject/signing-key" "^5.5.0" +"@ethersproject/transactions@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.7.0.tgz#91318fc24063e057885a6af13fdb703e1f993d3b" + integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ== + dependencies: + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + "@ethersproject/signing-key" "^5.7.0" + "@ethersproject/units@5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.5.0.tgz#104d02db5b5dc42cc672cc4587bafb87a95ee45e" @@ -635,6 +770,15 @@ "@ethersproject/constants" "^5.5.0" "@ethersproject/logger" "^5.5.0" +"@ethersproject/units@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.7.0.tgz#637b563d7e14f42deeee39245275d477aae1d8b1" + integrity sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/wallet@5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.5.0.tgz#322a10527a440ece593980dca6182f17d54eae75" @@ -667,6 +811,17 @@ "@ethersproject/properties" "^5.5.0" "@ethersproject/strings" "^5.5.0" +"@ethersproject/web@^5.7.0": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.7.1.tgz#de1f285b373149bee5928f4eb7bcb87ee5fbb4ae" + integrity sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w== + dependencies: + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + "@ethersproject/wordlists@5.5.0", "@ethersproject/wordlists@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.5.0.tgz#aac74963aa43e643638e5172353d931b347d584f" @@ -757,6 +912,18 @@ resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d" integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA== +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + "@lerna/add@3.21.0": version "3.21.0" resolved "https://registry.yarnpkg.com/@lerna/add/-/add-3.21.0.tgz#27007bde71cc7b0a2969ab3c2f0ae41578b4577b" @@ -1442,6 +1609,17 @@ npmlog "^4.1.2" write-file-atomic "^2.3.0" +"@metamask/eth-sig-util@^4.0.0": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz#3ad61f6ea9ad73ba5b19db780d40d9aae5157088" + integrity sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ== + dependencies: + ethereumjs-abi "^0.6.8" + ethereumjs-util "^6.2.1" + ethjs-util "^0.1.6" + tweetnacl "^1.0.3" + tweetnacl-util "^0.15.1" + "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" @@ -1455,6 +1633,40 @@ resolved "https://registry.yarnpkg.com/@multiformats/base-x/-/base-x-4.0.1.tgz#95ff0fa58711789d53aefb2590a8b7a4e715d121" integrity sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw== +"@noble/curves@1.2.0", "@noble/curves@~1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35" + integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw== + dependencies: + "@noble/hashes" "1.3.2" + +"@noble/curves@1.3.0", "@noble/curves@~1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.3.0.tgz#01be46da4fd195822dab821e72f71bf4aeec635e" + integrity sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA== + dependencies: + "@noble/hashes" "1.3.3" + +"@noble/hashes@1.2.0", "@noble/hashes@~1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12" + integrity sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ== + +"@noble/hashes@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" + integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== + +"@noble/hashes@1.3.3", "@noble/hashes@~1.3.0", "@noble/hashes@~1.3.2": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" + integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== + +"@noble/secp256k1@1.7.1", "@noble/secp256k1@~1.7.0": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" + integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -1481,6 +1693,157 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@nomicfoundation/edr-darwin-arm64@0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.3.5.tgz#3c428000ec27501617a00f7c447054a272f99177" + integrity sha512-gIXUIiPMUy6roLHpNlxf15DumU7/YhffUf7XIB+WUjMecaySfTGyZsTGnCMJZqrDyiYqWPyPKwCV/2u/jqFAUg== + +"@nomicfoundation/edr-darwin-x64@0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.3.5.tgz#fee26c4a83c9fc534bc0a719e88382fafeed69fe" + integrity sha512-0MrpOCXUK8gmplpYZ2Cy0holHEylvWoNeecFcrP2WJ5DLQzrB23U5JU2MvUzOJ7aL76Za1VXNBWi/UeTWdHM+w== + +"@nomicfoundation/edr-linux-arm64-gnu@0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.3.5.tgz#c48ad44b578abb50706cd8cad607a65b1289689f" + integrity sha512-aw9f7AZMiY1dZFNePJGKho2k+nEgFgzUAyyukiKfSqUIMXoFXMf1U3Ujv848czrSq9c5XGcdDa2xnEf3daU3xg== + +"@nomicfoundation/edr-linux-arm64-musl@0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.3.5.tgz#7cc1b12e2adf872e5a542647182262cc4582f8d9" + integrity sha512-cVFRQjyABBlsbDj+XTczYBfrCHprZ6YNzN8gGGSqAh+UGIJkAIRomK6ar27GyJLNx3HkgbuDoi/9kA0zOo/95w== + +"@nomicfoundation/edr-linux-x64-gnu@0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.3.5.tgz#ae4366c6fad03ea6ec3e2f2bafe397661c11f054" + integrity sha512-CjOg85DfR1Vt0fQWn5U0qi26DATK9tVzo3YOZEyI0JBsnqvk43fUTPv3uUAWBrPIRg5O5kOc9xG13hSpCBBxBg== + +"@nomicfoundation/edr-linux-x64-musl@0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.3.5.tgz#4071929fcece90f95c271f6ba455c55f35750efd" + integrity sha512-hvX8bBGpBydAVevzK8jsu2FlqVZK1RrCyTX6wGHnltgMuBaoGLHYtNHiFpteOaJw2byYMiORc2bvj+98LhJ0Ew== + +"@nomicfoundation/edr-win32-x64-msvc@0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.3.5.tgz#6e6684e56f336c67fbda9bf75b21d7ac586631b8" + integrity sha512-IJXjW13DY5UPsx/eG5DGfXtJ7Ydwrvw/BTZ2Y93lRLHzszVpSmeVmlxjZP5IW2afTSgMLaAAsqNw4NhppRGN8A== + +"@nomicfoundation/edr@^0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr/-/edr-0.3.5.tgz#04f980386f871e1c3bbc180b290d37af0336c81d" + integrity sha512-dPSM9DuI1sr71gqWUMgLo8MjHQWO4+WNDm3iWaT6P4vUFJReZX5qwA5X+3UwIPBry8GvNY084u7yWUvB3/8rqA== + optionalDependencies: + "@nomicfoundation/edr-darwin-arm64" "0.3.5" + "@nomicfoundation/edr-darwin-x64" "0.3.5" + "@nomicfoundation/edr-linux-arm64-gnu" "0.3.5" + "@nomicfoundation/edr-linux-arm64-musl" "0.3.5" + "@nomicfoundation/edr-linux-x64-gnu" "0.3.5" + "@nomicfoundation/edr-linux-x64-musl" "0.3.5" + "@nomicfoundation/edr-win32-x64-msvc" "0.3.5" + +"@nomicfoundation/ethereumjs-common@4.0.4": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-4.0.4.tgz#9901f513af2d4802da87c66d6f255b510bef5acb" + integrity sha512-9Rgb658lcWsjiicr5GzNCjI1llow/7r0k50dLL95OJ+6iZJcVbi15r3Y0xh2cIO+zgX0WIHcbzIu6FeQf9KPrg== + dependencies: + "@nomicfoundation/ethereumjs-util" "9.0.4" + +"@nomicfoundation/ethereumjs-rlp@5.0.4": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-5.0.4.tgz#66c95256fc3c909f6fb18f6a586475fc9762fa30" + integrity sha512-8H1S3s8F6QueOc/X92SdrA4RDenpiAEqMg5vJH99kcQaCy/a3Q6fgseo75mgWlbanGJXSlAPtnCeG9jvfTYXlw== + +"@nomicfoundation/ethereumjs-tx@5.0.4": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-5.0.4.tgz#b0ceb58c98cc34367d40a30d255d6315b2f456da" + integrity sha512-Xjv8wAKJGMrP1f0n2PeyfFCCojHd7iS3s/Ab7qzF1S64kxZ8Z22LCMynArYsVqiFx6rzYy548HNVEyI+AYN/kw== + dependencies: + "@nomicfoundation/ethereumjs-common" "4.0.4" + "@nomicfoundation/ethereumjs-rlp" "5.0.4" + "@nomicfoundation/ethereumjs-util" "9.0.4" + ethereum-cryptography "0.1.3" + +"@nomicfoundation/ethereumjs-util@9.0.4": + version "9.0.4" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-9.0.4.tgz#84c5274e82018b154244c877b76bc049a4ed7b38" + integrity sha512-sLOzjnSrlx9Bb9EFNtHzK/FJFsfg2re6bsGqinFinH1gCqVfz9YYlXiMWwDM4C/L4ywuHFCYwfKTVr/QHQcU0Q== + dependencies: + "@nomicfoundation/ethereumjs-rlp" "5.0.4" + ethereum-cryptography "0.1.3" + +"@nomicfoundation/hardhat-network-helpers@^1.0.10": + version "1.0.10" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-network-helpers/-/hardhat-network-helpers-1.0.10.tgz#c61042ceb104fdd6c10017859fdef6529c1d6585" + integrity sha512-R35/BMBlx7tWN5V6d/8/19QCwEmIdbnA4ZrsuXgvs8i2qFx5i7h6mH5pBS4Pwi4WigLH+upl6faYusrNPuzMrQ== + dependencies: + ethereumjs-util "^7.1.4" + +"@nomicfoundation/solidity-analyzer-darwin-arm64@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.1.tgz#4c858096b1c17fe58a474fe81b46815f93645c15" + integrity sha512-KcTodaQw8ivDZyF+D76FokN/HdpgGpfjc/gFCImdLUyqB6eSWVaZPazMbeAjmfhx3R0zm/NYVzxwAokFKgrc0w== + +"@nomicfoundation/solidity-analyzer-darwin-x64@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-darwin-x64/-/solidity-analyzer-darwin-x64-0.1.1.tgz#6e25ccdf6e2d22389c35553b64fe6f3fdaec432c" + integrity sha512-XhQG4BaJE6cIbjAVtzGOGbK3sn1BO9W29uhk9J8y8fZF1DYz0Doj8QDMfpMu+A6TjPDs61lbsmeYodIDnfveSA== + +"@nomicfoundation/solidity-analyzer-freebsd-x64@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-freebsd-x64/-/solidity-analyzer-freebsd-x64-0.1.1.tgz#0a224ea50317139caeebcdedd435c28a039d169c" + integrity sha512-GHF1VKRdHW3G8CndkwdaeLkVBi5A9u2jwtlS7SLhBc8b5U/GcoL39Q+1CSO3hYqePNP+eV5YI7Zgm0ea6kMHoA== + +"@nomicfoundation/solidity-analyzer-linux-arm64-gnu@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-arm64-gnu/-/solidity-analyzer-linux-arm64-gnu-0.1.1.tgz#dfa085d9ffab9efb2e7b383aed3f557f7687ac2b" + integrity sha512-g4Cv2fO37ZsUENQ2vwPnZc2zRenHyAxHcyBjKcjaSmmkKrFr64yvzeNO8S3GBFCo90rfochLs99wFVGT/0owpg== + +"@nomicfoundation/solidity-analyzer-linux-arm64-musl@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-arm64-musl/-/solidity-analyzer-linux-arm64-musl-0.1.1.tgz#c9e06b5d513dd3ab02a7ac069c160051675889a4" + integrity sha512-WJ3CE5Oek25OGE3WwzK7oaopY8xMw9Lhb0mlYuJl/maZVo+WtP36XoQTb7bW/i8aAdHW5Z+BqrHMux23pvxG3w== + +"@nomicfoundation/solidity-analyzer-linux-x64-gnu@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-x64-gnu/-/solidity-analyzer-linux-x64-gnu-0.1.1.tgz#8d328d16839e52571f72f2998c81e46bf320f893" + integrity sha512-5WN7leSr5fkUBBjE4f3wKENUy9HQStu7HmWqbtknfXkkil+eNWiBV275IOlpXku7v3uLsXTOKpnnGHJYI2qsdA== + +"@nomicfoundation/solidity-analyzer-linux-x64-musl@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-x64-musl/-/solidity-analyzer-linux-x64-musl-0.1.1.tgz#9b49d0634b5976bb5ed1604a1e1b736f390959bb" + integrity sha512-KdYMkJOq0SYPQMmErv/63CwGwMm5XHenEna9X9aB8mQmhDBrYrlAOSsIPgFCUSL0hjxE3xHP65/EPXR/InD2+w== + +"@nomicfoundation/solidity-analyzer-win32-arm64-msvc@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-win32-arm64-msvc/-/solidity-analyzer-win32-arm64-msvc-0.1.1.tgz#e2867af7264ebbcc3131ef837878955dd6a3676f" + integrity sha512-VFZASBfl4qiBYwW5xeY20exWhmv6ww9sWu/krWSesv3q5hA0o1JuzmPHR4LPN6SUZj5vcqci0O6JOL8BPw+APg== + +"@nomicfoundation/solidity-analyzer-win32-ia32-msvc@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-win32-ia32-msvc/-/solidity-analyzer-win32-ia32-msvc-0.1.1.tgz#0685f78608dd516c8cdfb4896ed451317e559585" + integrity sha512-JnFkYuyCSA70j6Si6cS1A9Gh1aHTEb8kOTBApp/c7NRTFGNMH8eaInKlyuuiIbvYFhlXW4LicqyYuWNNq9hkpQ== + +"@nomicfoundation/solidity-analyzer-win32-x64-msvc@0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-win32-x64-msvc/-/solidity-analyzer-win32-x64-msvc-0.1.1.tgz#c9a44f7108646f083b82e851486e0f6aeb785836" + integrity sha512-HrVJr6+WjIXGnw3Q9u6KQcbZCtk0caVWhCdFADySvRyUxJ8PnzlaP+MhwNE8oyT8OZ6ejHBRrrgjSqDCFXGirw== + +"@nomicfoundation/solidity-analyzer@^0.1.0": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.1.tgz#f5f4d36d3f66752f59a57e7208cd856f3ddf6f2d" + integrity sha512-1LMtXj1puAxyFusBgUIy5pZk3073cNXYnXUpuNKFghHbIit/xZgbk0AokpUADbNm3gyD6bFWl3LRFh3dhVdREg== + optionalDependencies: + "@nomicfoundation/solidity-analyzer-darwin-arm64" "0.1.1" + "@nomicfoundation/solidity-analyzer-darwin-x64" "0.1.1" + "@nomicfoundation/solidity-analyzer-freebsd-x64" "0.1.1" + "@nomicfoundation/solidity-analyzer-linux-arm64-gnu" "0.1.1" + "@nomicfoundation/solidity-analyzer-linux-arm64-musl" "0.1.1" + "@nomicfoundation/solidity-analyzer-linux-x64-gnu" "0.1.1" + "@nomicfoundation/solidity-analyzer-linux-x64-musl" "0.1.1" + "@nomicfoundation/solidity-analyzer-win32-arm64-msvc" "0.1.1" + "@nomicfoundation/solidity-analyzer-win32-ia32-msvc" "0.1.1" + "@nomicfoundation/solidity-analyzer-win32-x64-msvc" "0.1.1" + "@nomiclabs/buidler-ganache@^1.3.3": version "1.3.3" resolved "https://registry.yarnpkg.com/@nomiclabs/buidler-ganache/-/buidler-ganache-1.3.3.tgz#220beb381b0df45bf1860396df4a639267711066" @@ -1754,6 +2117,11 @@ dependencies: "@octokit/openapi-types" "^11.2.0" +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" @@ -1841,6 +2209,62 @@ debug "^3.1.0" hosted-git-info "^2.6.0" +"@scure/base@~1.1.0", "@scure/base@~1.1.2", "@scure/base@~1.1.4": + version "1.1.6" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.6.tgz#8ce5d304b436e4c84f896e0550c83e4d88cb917d" + integrity sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g== + +"@scure/bip32@1.1.5": + version "1.1.5" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.1.5.tgz#d2ccae16dcc2e75bc1d75f5ef3c66a338d1ba300" + integrity sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw== + dependencies: + "@noble/hashes" "~1.2.0" + "@noble/secp256k1" "~1.7.0" + "@scure/base" "~1.1.0" + +"@scure/bip32@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.2.tgz#90e78c027d5e30f0b22c1f8d50ff12f3fb7559f8" + integrity sha512-N1ZhksgwD3OBlwTv3R6KFEcPojl/W4ElJOeCZdi+vuI5QmTFwLq3OFf2zd2ROpKvxFdgZ6hUpb0dx9bVNEwYCA== + dependencies: + "@noble/curves" "~1.2.0" + "@noble/hashes" "~1.3.2" + "@scure/base" "~1.1.2" + +"@scure/bip32@1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.3.tgz#a9624991dc8767087c57999a5d79488f48eae6c8" + integrity sha512-LJaN3HwRbfQK0X1xFSi0Q9amqOgzQnnDngIt+ZlsBC3Bm7/nE7K0kwshZHyaru79yIVRv/e1mQAjZyuZG6jOFQ== + dependencies: + "@noble/curves" "~1.3.0" + "@noble/hashes" "~1.3.2" + "@scure/base" "~1.1.4" + +"@scure/bip39@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.1.1.tgz#b54557b2e86214319405db819c4b6a370cf340c5" + integrity sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg== + dependencies: + "@noble/hashes" "~1.2.0" + "@scure/base" "~1.1.0" + +"@scure/bip39@1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.1.tgz#5cee8978656b272a917b7871c981e0541ad6ac2a" + integrity sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg== + dependencies: + "@noble/hashes" "~1.3.0" + "@scure/base" "~1.1.0" + +"@scure/bip39@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.2.tgz#f3426813f4ced11a47489cbcf7294aa963966527" + integrity sha512-HYf9TUXG80beW+hGAt3TRM8wU6pQoYur9iNypTROm42dorCGmLnFe3eWjz3gOq6G62H2WRh0FCzAR1PI+29zIA== + dependencies: + "@noble/hashes" "~1.3.2" + "@scure/base" "~1.1.4" + "@sentry/core@5.30.0": version "5.30.0" resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.30.0.tgz#6b203664f69e75106ee8b5a2fe1d717379b331f3" @@ -1919,11 +2343,6 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== -"@solidity-parser/parser@^0.11.0": - version "0.11.1" - resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.11.1.tgz#fa840af64840c930f24a9c82c08d4a092a068add" - integrity sha512-H8BSBoKE8EubJa0ONqecA2TviT3TnHeC4NpgnAHSUiuhZoQBfPB4L2P9bs8R6AoTW10Endvh3vc+fomVMIDIYQ== - "@solidity-parser/parser@^0.12.0", "@solidity-parser/parser@^0.12.2": version "0.12.2" resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.12.2.tgz#1afad367cb29a2ed8cdd4a3a62701c2821fb578f" @@ -1936,12 +2355,10 @@ dependencies: antlr4ts "^0.5.0-alpha.4" -"@solidity-parser/parser@^0.14.0": - version "0.14.1" - resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.14.1.tgz#179afb29f4e295a77cc141151f26b3848abc3c46" - integrity sha512-eLjj2L6AuQjBB6s/ibwCAc0DwrR5Ge+ys+wgWo+bviU7fV2nTMQhU63CGaDKXg9iTmMxwhkyoggdIR7ZGRfMgw== - dependencies: - antlr4ts "^0.5.0-alpha.4" +"@solidity-parser/parser@^0.18.0": + version "0.18.0" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.18.0.tgz#8e77a02a09ecce957255a2f48c9a7178ec191908" + integrity sha512-yfORGUIPgLck41qyN7nbwJRAx17/jAIXCTanHOJZhB6PJ1iAk/84b/xlsVKFSyNyLXIj0dhppoE0+CRws7wlzA== "@solidity-parser/parser@^0.5.2": version "0.5.2" @@ -2159,11 +2576,6 @@ xhr "^2.2.0" xtend "^4.0.1" -"@types/abstract-leveldown@*": - version "5.0.2" - resolved "https://registry.yarnpkg.com/@types/abstract-leveldown/-/abstract-leveldown-5.0.2.tgz#ee81917fe38f770e29eec8139b6f16ee4a8b0a5f" - integrity sha512-+jA1XXF3jsz+Z7FcuiNqgK53hTa/luglT2TyTpKPqoYbxVY+mCPF22Rm+q3KPBrMHJwNXFrTViHszBOfU4vftQ== - "@types/bignumber.js@^5.0.0": version "5.0.0" resolved "https://registry.yarnpkg.com/@types/bignumber.js/-/bignumber.js-5.0.0.tgz#d9f1a378509f3010a3255e9cc822ad0eeb4ab969" @@ -2212,20 +2624,6 @@ "@types/minimatch" "*" "@types/node" "*" -"@types/level-errors@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/level-errors/-/level-errors-3.0.0.tgz#15c1f4915a5ef763b51651b15e90f6dc081b96a8" - integrity sha512-/lMtoq/Cf/2DVOm6zE6ORyOM+3ZVm/BvzEZVxUhf6bgh8ZHglXlBqxbxSlJeVp8FCbD3IVvk/VbsaNmDjrQvqQ== - -"@types/levelup@^4.3.0": - version "4.3.3" - resolved "https://registry.yarnpkg.com/@types/levelup/-/levelup-4.3.3.tgz#4dc2b77db079b1cf855562ad52321aa4241b8ef4" - integrity sha512-K+OTIjJcZHVlZQN1HmU64VtrC0jC3dXWQozuEIR9zVvltIk90zaGPM2AgT+fIkChpzHhFE3YnvFLCbLtzAmexA== - dependencies: - "@types/abstract-leveldown" "*" - "@types/level-errors" "*" - "@types/node" "*" - "@types/long@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9" @@ -2322,6 +2720,11 @@ abbrev@1.0.x: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" integrity sha1-kbR5JYinc4wl813W9jdSovh3YTU= +abitype@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.0.tgz#237176dace81d90d018bebf3a45cb42f2a2d9e97" + integrity sha512-NMeMah//6bJ56H5XRj8QCV4AwuW6hB6zqz2LnhhLdcWVQOsXki6/Pn3APeqxCma62nXIcmZWdu1DlHWS74umVQ== + abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" @@ -2350,17 +2753,6 @@ abstract-leveldown@^5.0.0, abstract-leveldown@~5.0.0: dependencies: xtend "~4.0.0" -abstract-leveldown@^6.2.1: - version "6.3.0" - resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.3.0.tgz#d25221d1e6612f820c35963ba4bd739928f6026a" - integrity sha512-TU5nlYgta8YrBMNpc9FwQzRbiXsj49gsALsXadbGHt9CROPzX5fB0rWDR5mtdpOOKa5XqRFpbj1QroPAoPzVjQ== - dependencies: - buffer "^5.5.0" - immediate "^3.2.3" - level-concat-iterator "~2.0.0" - level-supports "~1.0.0" - xtend "~4.0.0" - abstract-leveldown@~2.6.0: version "2.6.3" resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-2.6.3.tgz#1c5e8c6a5ef965ae8c35dfb3a8770c476b82c4b8" @@ -2368,17 +2760,6 @@ abstract-leveldown@~2.6.0: dependencies: xtend "~4.0.0" -abstract-leveldown@~6.2.1: - version "6.2.3" - resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz#036543d87e3710f2528e47040bc3261b77a9a8eb" - integrity sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ== - dependencies: - buffer "^5.5.0" - immediate "^3.2.3" - level-concat-iterator "~2.0.0" - level-supports "~1.0.0" - xtend "~4.0.0" - accepts@~1.3.7: version "1.3.7" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" @@ -2435,6 +2816,14 @@ agentkeepalive@^3.4.1: dependencies: humanize-ms "^1.2.1" +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + ajv@^5.2.2: version "5.5.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" @@ -2470,12 +2859,19 @@ amdefine@>=0.0.4: resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= +ansi-align@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" + integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== + dependencies: + string-width "^4.1.0" + ansi-colors@3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== -ansi-colors@^4.1.0, ansi-colors@^4.1.1: +ansi-colors@4.1.1, ansi-colors@^4.1.0, ansi-colors@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== @@ -2523,6 +2919,11 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" @@ -2542,6 +2943,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + antlr4ts@^0.5.0-alpha.4: version "0.5.0-alpha.4" resolved "https://registry.yarnpkg.com/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz#71702865a87478ed0b40c0709f422cf14d51652a" @@ -2615,6 +3021,11 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + arr-diff@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" @@ -2669,7 +3080,7 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -array-uniq@1.0.3, array-uniq@^1.0.1, array-uniq@^1.0.3: +array-uniq@^1.0.1, array-uniq@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= @@ -2736,7 +3147,7 @@ async-each@^1.0.0: resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== -async-eventemitter@^0.2.2, async-eventemitter@^0.2.4: +async-eventemitter@^0.2.2: version "0.2.4" resolved "https://registry.yarnpkg.com/async-eventemitter/-/async-eventemitter-0.2.4.tgz#f5e7c8ca7d3e46aab9ec40a292baf686a0bafaca" integrity sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw== @@ -2802,6 +3213,15 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== +axios@^1.6.7: + version "1.6.8" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66" + integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" @@ -3503,6 +3923,11 @@ bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2, bn.js@^5.1.3, bn.js@^5.2.0: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== +bn.js@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== + body-parser@1.19.0, body-parser@^1.16.0: version "1.19.0" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" @@ -3550,6 +3975,20 @@ borc@^3.0.0: json-text-sequence "~0.3.0" readable-stream "^3.6.0" +boxen@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" + integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ== + dependencies: + ansi-align "^3.0.0" + camelcase "^6.2.0" + chalk "^4.1.0" + cli-boxes "^2.2.1" + string-width "^4.2.2" + type-fest "^0.20.2" + widest-line "^3.1.0" + wrap-ansi "^7.0.0" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -3558,6 +3997,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + braces@^1.8.2: version "1.8.5" resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" @@ -3595,6 +4041,11 @@ brorand@^1.0.1, brorand@^1.1.0: resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= +brotli-wasm@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brotli-wasm/-/brotli-wasm-2.0.1.tgz#2b3f4dc3db0c3e60d2635c055e6bac4ddf4bd3f5" + integrity sha512-+3USgYsC7bzb5yU0/p2HnnynZl0ak0E6uoIm4UW4Aby/8s8HFCq6NCfrrf1E9c3O8OCSzq3oYO1tUVqIi61Nww== + browser-readablestream-to-it@^1.0.1, browser-readablestream-to-it@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/browser-readablestream-to-it/-/browser-readablestream-to-it-1.0.3.tgz#ac3e406c7ee6cdf0a502dd55db33bab97f7fba76" @@ -3976,6 +4427,11 @@ camelcase@^5.0.0, camelcase@^5.3.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== +camelcase@^6.0.0, camelcase@^6.2.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30001265: version "1.0.30001270" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001270.tgz#cc9c37a4ec5c1a8d616fc7bace902bb053b0cdea" @@ -4015,6 +4471,14 @@ chai@^4.2.0: pathval "^1.1.1" type-detect "^4.0.5" +chalk@4.1.2, chalk@^4.1.0, chalk@^4.1.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -4035,14 +4499,6 @@ chalk@^2.0.0, chalk@^2.3.1, chalk@^2.3.2, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.1.1: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -4104,6 +4560,21 @@ chokidar@3.3.0: optionalDependencies: fsevents "~2.1.1" +chokidar@3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + chokidar@^1.6.0: version "1.7.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" @@ -4189,6 +4660,16 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + +cli-boxes@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" + integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== + cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" @@ -4206,6 +4687,15 @@ cli-table3@^0.5.0: optionalDependencies: colors "^1.1.2" +cli-table3@^0.6.3: + version "0.6.4" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.4.tgz#d1c536b8a3f2e7bec58f67ac9e5769b1b30088b0" + integrity sha512-Lm3L0p+/npIQWNIiyF/nAn7T5dnOwR3xNTHXYEBFBFVPXzCVNZ5lqEC/1eo/EVfpDsQ1I+TX4ORPQgp+UI0CRw== + dependencies: + string-width "^4.2.0" + optionalDependencies: + "@colors/colors" "1.5.0" + cli-width@^2.0.0: version "2.2.1" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" @@ -4229,6 +4719,15 @@ cliui@^5.0.0: strip-ansi "^5.2.0" wrap-ansi "^5.1.0" +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + clone-buffer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" @@ -4316,7 +4815,7 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -colors@1.4.0, colors@^1.1.2: +colors@^1.1.2: version "1.4.0" resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== @@ -4678,7 +5177,7 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.3: +cross-spawn@^7.0.0, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -4797,6 +5296,13 @@ debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: dependencies: ms "2.1.2" +debug@4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + debug@^3.1.0: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" @@ -4822,6 +5328,11 @@ decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= +decamelize@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" + integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== + decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" @@ -4948,14 +5459,6 @@ deferred-leveldown@~4.0.0: abstract-leveldown "~5.0.0" inherits "^2.0.3" -deferred-leveldown@~5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz#27a997ad95408b61161aa69bd489b86c71b78058" - integrity sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw== - dependencies: - abstract-leveldown "~6.2.1" - inherits "^2.0.3" - define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" @@ -5076,6 +5579,11 @@ diff@3.5.0, diff@^3.5.0: resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== +diff@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" + integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== + diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" @@ -5085,6 +5593,13 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" +difflib@^0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/difflib/-/difflib-0.2.4.tgz#b5e30361a6db023176d562892db85940a718f47e" + integrity sha512-9YVwmMb0wQHQNr5J9m6BSj6fk4pfGITGQOOs+D9Fl+INODWFOfvhIU1hNv6GgR1RBoC/9NJcwu77zShxV0kT7w== + dependencies: + heap ">= 0.2.0" + dir-glob@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" @@ -5211,6 +5726,11 @@ duplexify@^3.4.2, duplexify@^3.6.0: readable-stream "^2.0.0" stream-shift "^1.0.0" +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -5269,6 +5789,11 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -5285,16 +5810,6 @@ encoding-down@5.0.4, encoding-down@~5.0.0: level-errors "^2.0.0" xtend "^4.0.1" -encoding-down@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/encoding-down/-/encoding-down-6.3.0.tgz#b1c4eb0e1728c146ecaef8e32963c549e76d082b" - integrity sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw== - dependencies: - abstract-leveldown "^6.2.1" - inherits "^2.0.3" - level-codec "^9.0.0" - level-errors "^2.0.0" - encoding@^0.1.11, encoding@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" @@ -5448,6 +5963,11 @@ escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1 resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +escape-string-regexp@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + escodegen@1.8.x: version "1.8.1" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" @@ -5539,27 +6059,6 @@ eth-gas-reporter@^0.2.18: sha1 "^1.1.1" sync-request "^6.0.0" -eth-gas-reporter@^0.2.24: - version "0.2.24" - resolved "https://registry.yarnpkg.com/eth-gas-reporter/-/eth-gas-reporter-0.2.24.tgz#768721fec7de02b566e4ebfd123466d275d7035c" - integrity sha512-RbXLC2bnuPHzIMU/rnLXXlb6oiHEEKu7rq2UrAX/0mfo0Lzrr/kb9QTjWjfz8eNvc+uu6J8AuBwI++b+MLNI2w== - dependencies: - "@ethersproject/abi" "^5.0.0-beta.146" - "@solidity-parser/parser" "^0.14.0" - cli-table3 "^0.5.0" - colors "1.4.0" - ethereumjs-util "6.2.0" - ethers "^4.0.40" - fs-readdir-recursive "^1.1.0" - lodash "^4.17.14" - markdown-table "^1.1.3" - mocha "^7.1.1" - req-cwd "^2.0.0" - request "^2.88.0" - request-promise-native "^1.0.5" - sha1 "^1.1.1" - sync-request "^6.0.0" - eth-json-rpc-errors@^1.0.1: version "1.1.1" resolved "https://registry.yarnpkg.com/eth-json-rpc-errors/-/eth-json-rpc-errors-1.1.1.tgz#148377ef55155585981c21ff574a8937f9d6991f" @@ -5731,7 +6230,7 @@ ethereum-common@^0.0.18: resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.0.18.tgz#2fdc3576f232903358976eb39da783213ff9523f" integrity sha1-L9w1dvIykDNYl26znaeDIT/5Uj8= -ethereum-cryptography@^0.1.2, ethereum-cryptography@^0.1.3: +ethereum-cryptography@0.1.3, ethereum-cryptography@^0.1.2, ethereum-cryptography@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" integrity sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ== @@ -5752,6 +6251,26 @@ ethereum-cryptography@^0.1.2, ethereum-cryptography@^0.1.3: secp256k1 "^4.0.1" setimmediate "^1.0.5" +ethereum-cryptography@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz#5ccfa183e85fdaf9f9b299a79430c044268c9b3a" + integrity sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw== + dependencies: + "@noble/hashes" "1.2.0" + "@noble/secp256k1" "1.7.1" + "@scure/bip32" "1.1.5" + "@scure/bip39" "1.1.1" + +ethereum-cryptography@^2.0.0, ethereum-cryptography@^2.1.2, ethereum-cryptography@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-2.1.3.tgz#1352270ed3b339fe25af5ceeadcf1b9c8e30768a" + integrity sha512-BlwbIL7/P45W8FGW2r7LGuvoEZ+7PWsniMvQ4p5s2xCyw9tmaDlpfsN9HjAucbF+t/qpVHwZUisgfK24TCW8aA== + dependencies: + "@noble/curves" "1.3.0" + "@noble/hashes" "1.3.3" + "@scure/bip32" "1.3.3" + "@scure/bip39" "1.2.2" + ethereum-ens@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/ethereum-ens/-/ethereum-ens-0.8.0.tgz#6d0f79acaa61fdbc87d2821779c4e550243d4c57" @@ -5887,7 +6406,7 @@ ethereumjs-util@6.2.0: rlp "^2.2.3" secp256k1 "^3.0.1" -ethereumjs-util@6.2.1, ethereumjs-util@^6.0.0, ethereumjs-util@^6.1.0, ethereumjs-util@^6.2.0: +ethereumjs-util@6.2.1, ethereumjs-util@^6.0.0, ethereumjs-util@^6.1.0, ethereumjs-util@^6.2.0, ethereumjs-util@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69" integrity sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw== @@ -6088,7 +6607,7 @@ ethjs-unit@0.1.6: bn.js "4.11.6" number-to-bn "1.7.0" -ethjs-util@0.1.6, ethjs-util@^0.1.3: +ethjs-util@0.1.6, ethjs-util@^0.1.3, ethjs-util@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.6.tgz#f308b62f185f9fe6237132fb2a9818866a5cd536" integrity sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w== @@ -6510,6 +7029,14 @@ find-up@3.0.0, find-up@^3.0.0: dependencies: locate-path "^3.0.0" +find-up@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + find-up@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" @@ -6555,6 +7082,11 @@ flat@^4.1.0: dependencies: is-buffer "~2.0.3" +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + flow-stoplight@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/flow-stoplight/-/flow-stoplight-1.0.0.tgz#4a292c5bcff8b39fa6cc0cb1a853d86f27eeff7b" @@ -6573,6 +7105,11 @@ follow-redirects@^1.12.1: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.4.tgz#838fdf48a8bbdd79e52ee51fb1c94e3ed98b9379" integrity sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g== +follow-redirects@^1.15.6: + version "1.15.6" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" + integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== + for-each@^0.3.3, for-each@~0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -6597,6 +7134,14 @@ foreach@^2.0.5: resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= +foreground-child@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" + integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -6840,7 +7385,7 @@ get-caller-file@^1.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== -get-caller-file@^2.0.1: +get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -7081,26 +7626,48 @@ glob@7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^5.0.15: - version "5.0.15" - resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" - integrity sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E= +glob@7.2.0, glob@^7.0.0, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== dependencies: + fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "2 || 3" + minimatch "^3.0.4" once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.0, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4: - version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== +glob@8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "^3.0.4" + minimatch "^5.0.1" + once "^1.3.0" + +glob@^10.3.10: + version "10.3.12" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.12.tgz#3a65c363c2e9998d220338e88a5f6ac97302960b" + integrity sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^2.3.6" + minimatch "^9.0.1" + minipass "^7.0.4" + path-scurry "^1.10.2" + +glob@^5.0.15: + version "5.0.15" + resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" + integrity sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E= + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" once "^1.3.0" path-is-absolute "^1.0.0" @@ -7298,66 +7865,74 @@ hardhat-abi-exporter@^2.8.0: "@ethersproject/abi" "^5.5.0" delete-empty "^3.0.0" -hardhat-gas-reporter@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/hardhat-gas-reporter/-/hardhat-gas-reporter-1.0.8.tgz#93ce271358cd748d9c4185dbb9d1d5525ec145e0" - integrity sha512-1G5thPnnhcwLHsFnl759f2tgElvuwdkzxlI65fC9PwxYMEe9cmjkVAAWTf3/3y8uP6ZSPiUiOW8PgZnykmZe0g== - dependencies: - array-uniq "1.0.3" - eth-gas-reporter "^0.2.24" +hardhat-gas-reporter@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/hardhat-gas-reporter/-/hardhat-gas-reporter-2.1.1.tgz#fc91d437ee841ff32df1340025b0198201df92da" + integrity sha512-h3PAU7iDiPNqPA4i7GcKgnIZsN3UIdALM4gdJNoTtfmF9PGVne5nFZRNX1N7Y2Mx+znbBqeV1mS7o/i6T7NkTg== + dependencies: + "@ethersproject/abi" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/units" "^5.7.0" + "@solidity-parser/parser" "^0.18.0" + axios "^1.6.7" + brotli-wasm "^2.0.1" + chalk "4.1.2" + cli-table3 "^0.6.3" + ethereum-cryptography "^2.1.3" + glob "^10.3.10" + jsonschema "^1.4.1" + lodash "^4.17.21" + markdown-table "2.0.0" sha1 "^1.1.1" + viem "2.7.14" -hardhat@^2.6.5: - version "2.6.6" - resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.6.6.tgz#11d2dc4c1659fcbb8632de9399b1b4999f8b3628" - integrity sha512-GneZAwvNxf3cNobYTmMSp2qGX/yJyUmHD/xjW+8zF2AgWkLS33CHmGn4aRFKqfjsk7BS7FU6jqMmmZhHHO3gUw== +hardhat@^2.22.2: + version "2.22.3" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.22.3.tgz#50605daca6b29862397e446c42ec14c89430bec3" + integrity sha512-k8JV2ECWNchD6ahkg2BR5wKVxY0OiKot7fuxiIpRK0frRqyOljcR2vKwgWSLw6YIeDcNNA4xybj7Og7NSxr2hA== dependencies: - "@ethereumjs/block" "^3.4.0" - "@ethereumjs/blockchain" "^5.4.0" - "@ethereumjs/common" "^2.4.0" - "@ethereumjs/tx" "^3.3.0" - "@ethereumjs/vm" "^5.5.2" "@ethersproject/abi" "^5.1.2" + "@metamask/eth-sig-util" "^4.0.0" + "@nomicfoundation/edr" "^0.3.5" + "@nomicfoundation/ethereumjs-common" "4.0.4" + "@nomicfoundation/ethereumjs-tx" "5.0.4" + "@nomicfoundation/ethereumjs-util" "9.0.4" + "@nomicfoundation/solidity-analyzer" "^0.1.0" "@sentry/node" "^5.18.1" - "@solidity-parser/parser" "^0.11.0" "@types/bn.js" "^5.1.0" "@types/lru-cache" "^5.1.0" - abort-controller "^3.0.0" adm-zip "^0.4.16" + aggregate-error "^3.0.0" ansi-escapes "^4.3.0" + boxen "^5.1.2" chalk "^2.4.2" chokidar "^3.4.0" ci-info "^2.0.0" debug "^4.1.1" enquirer "^2.3.0" env-paths "^2.2.0" - eth-sig-util "^2.5.2" - ethereum-cryptography "^0.1.2" + ethereum-cryptography "^1.0.3" ethereumjs-abi "^0.6.8" - ethereumjs-util "^7.1.0" find-up "^2.1.0" fp-ts "1.19.3" fs-extra "^7.0.1" - glob "^7.1.3" - https-proxy-agent "^5.0.0" + glob "7.2.0" immutable "^4.0.0-rc.12" io-ts "1.10.4" + keccak "^3.0.2" lodash "^4.17.11" - merkle-patricia-tree "^4.2.0" mnemonist "^0.38.0" - mocha "^7.1.2" - node-fetch "^2.6.0" - qs "^6.7.0" + mocha "^10.0.0" + p-map "^4.0.0" raw-body "^2.4.1" resolve "1.17.0" semver "^6.3.0" - slash "^3.0.0" solc "0.7.3" source-map-support "^0.5.13" stacktrace-parser "^0.1.10" - "true-case-path" "^2.2.1" tsort "0.0.1" - uuid "^3.3.2" + undici "^5.14.0" + uuid "^8.3.2" ws "^7.4.6" has-ansi@^2.0.0: @@ -7499,6 +8074,11 @@ heap@0.2.6: resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.6.tgz#087e1f10b046932fc8594dd9e6d378afc9d1e5ac" integrity sha1-CH4fELBGky/IWU3Z5tN4r8nR5aw= +"heap@>= 0.2.0": + version "0.2.7" + resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.7.tgz#1e6adf711d3f27ce35a81fe3b7bd576c2260a8fc" + integrity sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg== + highlight.js@^10.4.1: version "10.7.3" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" @@ -8501,6 +9081,11 @@ is-unc-path@^1.0.0: dependencies: unc-path-regex "^0.1.2" +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + is-utf8@^0.2.0, is-utf8@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" @@ -8580,6 +9165,11 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= +isows@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.3.tgz#93c1cf0575daf56e7120bab5c8c448b0809d0d74" + integrity sha512-2cKei4vlmg2cxEjm3wVSqn8pcoRF/LX/wpifuuNquFO4SQmPwarClT+SUCA2lt+l581tTeZIPIZuIDo2jWN1fg== + isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -8674,6 +9264,15 @@ it-to-stream@^1.0.0: p-fifo "^1.0.0" readable-stream "^3.6.0" +jackspeak@^2.3.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.6.tgz#647ecc472238aee4b06ac0e461acc21a8c505ca8" + integrity sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + js-sha3@0.5.7, js-sha3@^0.5.7: version "0.5.7" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" @@ -8715,6 +9314,13 @@ js-yaml@3.x, js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -8867,6 +9473,11 @@ jsonschema@^1.2.4: resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.4.0.tgz#1afa34c4bc22190d8e42271ec17ac8b3404f87b2" integrity sha512-/YgW6pRMr6M7C+4o8kS+B/2myEpHCrxO4PEWnqJNBFMjn7EWXqlQ4tGwL6xTHeRplwuZmcAncdvfOad1nT2yMw== +jsonschema@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.4.1.tgz#cc4c3f0077fb4542982973d8a083b6b34f482dab" + integrity sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ== + jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -8904,6 +9515,15 @@ keccak@^3.0.0: node-gyp-build "^4.2.0" readable-stream "^3.6.0" +keccak@^3.0.2: + version "3.0.4" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.4.tgz#edc09b89e633c0549da444432ecf062ffadee86d" + integrity sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q== + dependencies: + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + readable-stream "^3.6.0" + keyv@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.0.0.tgz#44923ba39e68b12a7cec7df6c3268c031f2ef373" @@ -9034,11 +9654,6 @@ level-codec@~7.0.0: resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-7.0.1.tgz#341f22f907ce0f16763f24bddd681e395a0fb8a7" integrity sha512-Ua/R9B9r3RasXdRmOtd+t9TCOEIIlts+TN/7XTT2unhDaL6sJn83S3rUyljbr6lVtw49N3/yA0HHjpV6Kzb2aQ== -level-concat-iterator@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz#1d1009cf108340252cb38c51f9727311193e6263" - integrity sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw== - level-errors@^1.0.3: version "1.1.2" resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-1.1.2.tgz#4399c2f3d3ab87d0625f7e3676e2d807deff404d" @@ -9088,15 +9703,6 @@ level-iterator-stream@~3.0.0: readable-stream "^2.3.6" xtend "^4.0.0" -level-iterator-stream@~4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-4.0.2.tgz#7ceba69b713b0d7e22fcc0d1f128ccdc8a24f79c" - integrity sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q== - dependencies: - inherits "^2.0.4" - readable-stream "^3.4.0" - xtend "^4.0.2" - level-mem@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/level-mem/-/level-mem-3.0.1.tgz#7ce8cf256eac40f716eb6489654726247f5a89e5" @@ -9105,22 +9711,6 @@ level-mem@^3.0.1: level-packager "~4.0.0" memdown "~3.0.0" -level-mem@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/level-mem/-/level-mem-5.0.1.tgz#c345126b74f5b8aa376dc77d36813a177ef8251d" - integrity sha512-qd+qUJHXsGSFoHTziptAKXoLX87QjR7v2KMbqncDXPxQuCdsQlzmyX+gwrEHhlzn08vkf8TyipYyMmiC6Gobzg== - dependencies: - level-packager "^5.0.3" - memdown "^5.0.0" - -level-packager@^5.0.3: - version "5.1.1" - resolved "https://registry.yarnpkg.com/level-packager/-/level-packager-5.1.1.tgz#323ec842d6babe7336f70299c14df2e329c18939" - integrity sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ== - dependencies: - encoding-down "^6.3.0" - levelup "^4.3.2" - level-packager@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/level-packager/-/level-packager-4.0.1.tgz#7e7d3016af005be0869bc5fa8de93d2a7f56ffe6" @@ -9152,13 +9742,6 @@ level-sublevel@6.6.4: typewiselite "~1.0.0" xtend "~4.0.0" -level-supports@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-1.0.1.tgz#2f530a596834c7301622521988e2c36bb77d122d" - integrity sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg== - dependencies: - xtend "^4.0.2" - level-ws@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-0.0.0.tgz#372e512177924a00424b0b43aef2bb42496d228b" @@ -9176,15 +9759,6 @@ level-ws@^1.0.0: readable-stream "^2.2.8" xtend "^4.0.1" -level-ws@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-2.0.0.tgz#207a07bcd0164a0ec5d62c304b4615c54436d339" - integrity sha512-1iv7VXx0G9ec1isqQZ7y5LmoZo/ewAsyDHNA8EFDW5hqH2Kqovm33nSFkSdnLLAK+I5FlT+lo5Cw9itGe+CpQA== - dependencies: - inherits "^2.0.3" - readable-stream "^3.1.0" - xtend "^4.0.1" - levelup@3.1.1, levelup@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/levelup/-/levelup-3.1.1.tgz#c2c0b3be2b4dc316647c53b42e2f559e232d2189" @@ -9208,17 +9782,6 @@ levelup@^1.2.1: semver "~5.4.1" xtend "~4.0.0" -levelup@^4.3.2: - version "4.4.0" - resolved "https://registry.yarnpkg.com/levelup/-/levelup-4.4.0.tgz#f89da3a228c38deb49c48f88a70fb71f01cafed6" - integrity sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ== - dependencies: - deferred-leveldown "~5.3.0" - level-errors "~2.0.0" - level-iterator-stream "~4.0.0" - level-supports "~1.0.0" - xtend "~4.0.0" - levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" @@ -9287,6 +9850,13 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + lodash._reinterpolate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" @@ -9394,6 +9964,14 @@ log-symbols@3.0.0: dependencies: chalk "^2.4.2" +log-symbols@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + long@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" @@ -9446,6 +10024,11 @@ lru-cache@5.1.1, lru-cache@^5.1.1: dependencies: yallist "^3.0.2" +lru-cache@^10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.0.tgz#0bd445ca57363465900f4d1f9bd8db343a4d95c3" + integrity sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q== + lru-cache@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-3.2.0.tgz#71789b3b7f5399bec8565dda38aa30d2a097efee" @@ -9554,6 +10137,13 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" +markdown-table@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-2.0.0.tgz#194a90ced26d31fe753d8b9434430214c011865b" + integrity sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A== + dependencies: + repeat-string "^1.0.0" + markdown-table@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-1.1.3.tgz#9fcb69bcfdb8717bfd0398c6ec2d93036ef8de60" @@ -9564,11 +10154,6 @@ math-random@^1.0.1: resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c" integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A== -mcl-wasm@^0.7.1: - version "0.7.9" - resolved "https://registry.yarnpkg.com/mcl-wasm/-/mcl-wasm-0.7.9.tgz#c1588ce90042a8700c3b60e40efb339fc07ab87f" - integrity sha512-iJIUcQWA88IJB/5L15GnJVnSQJmf/YaxxV6zRavv83HILHaJQb6y0iFyDMdDO0gN8X37tdxmAOrH/P8B6RB8sQ== - md5.js@^1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" @@ -9611,18 +10196,6 @@ memdown@^1.0.0: ltgt "~2.2.0" safe-buffer "~5.1.1" -memdown@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/memdown/-/memdown-5.1.0.tgz#608e91a9f10f37f5b5fe767667a8674129a833cb" - integrity sha512-B3J+UizMRAlEArDjWHTMmadet+UKwHd3UjMgGBkZcKAxAYVPS9o0Yeiha4qvz7iGiL2Sb3igUft6p7nbFWctpw== - dependencies: - abstract-leveldown "~6.2.1" - functional-red-black-tree "~1.0.1" - immediate "~3.2.3" - inherits "~2.0.1" - ltgt "~2.2.0" - safe-buffer "~5.2.0" - memdown@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/memdown/-/memdown-3.0.0.tgz#93aca055d743b20efc37492e9e399784f2958309" @@ -9737,24 +10310,16 @@ merkle-patricia-tree@^2.1.2, merkle-patricia-tree@^2.3.2: rlp "^2.0.0" semaphore ">=1.0.1" -merkle-patricia-tree@^4.2.0, merkle-patricia-tree@^4.2.1: - version "4.2.2" - resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-4.2.2.tgz#6dec17855370172458244c2f42c989dd60b773a3" - integrity sha512-eqZYNTshcYx9aESkSPr71EqwsR/QmpnObDEV4iLxkt/x/IoLYZYjJvKY72voP/27Vy61iMOrfOG6jrn7ttXD+Q== - dependencies: - "@types/levelup" "^4.3.0" - ethereumjs-util "^7.1.2" - level-mem "^5.0.1" - level-ws "^2.0.0" - readable-stream "^3.6.0" - rlp "^2.2.4" - semaphore-async-await "^1.5.1" - methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= +micro-ftch@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/micro-ftch/-/micro-ftch-0.3.1.tgz#6cb83388de4c1f279a034fb0cf96dfc050853c5f" + integrity sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg== + micromatch@^2.1.5: version "2.3.11" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" @@ -9878,6 +10443,27 @@ minimalistic-crypto-utils@^1.0.1: dependencies: brace-expansion "^1.1.7" +minimatch@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" + integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^9.0.1: + version "9.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51" + integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== + dependencies: + brace-expansion "^2.0.1" + minimist-options@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" @@ -9913,6 +10499,11 @@ minipass@^2.3.5, minipass@^2.6.0, minipass@^2.9.0: safe-buffer "^5.1.2" yallist "^3.0.0" +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.4: + version "7.0.4" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c" + integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== + minizlib@^1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" @@ -9977,6 +10568,32 @@ mnemonist@^0.38.0: dependencies: obliterator "^1.6.1" +mocha@^10.0.0, mocha@^10.2.0: + version "10.4.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.4.0.tgz#ed03db96ee9cfc6d20c56f8e2af07b961dbae261" + integrity sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA== + dependencies: + ansi-colors "4.1.1" + browser-stdout "1.3.1" + chokidar "3.5.3" + debug "4.3.4" + diff "5.0.0" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "8.1.0" + he "1.2.0" + js-yaml "4.1.0" + log-symbols "4.1.0" + minimatch "5.0.1" + ms "2.1.3" + serialize-javascript "6.0.0" + strip-json-comments "3.1.1" + supports-color "8.1.1" + workerpool "6.2.1" + yargs "16.2.0" + yargs-parser "20.2.4" + yargs-unparser "2.0.0" + mocha@^4.0.1: version "4.1.0" resolved "https://registry.yarnpkg.com/mocha/-/mocha-4.1.0.tgz#7d86cfbcf35cb829e2754c32e17355ec05338794" @@ -10060,7 +10677,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.0.0, ms@^2.1.1: +ms@2.1.3, ms@^2.0.0, ms@^2.1.1: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -10811,6 +11428,13 @@ p-limit@^2.0.0, p-limit@^2.2.0: dependencies: p-try "^2.0.0" +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" @@ -10832,6 +11456,13 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + p-map-series@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-1.0.0.tgz#bf98fe575705658a9e1351befb85ae4c1f07bdca" @@ -10844,6 +11475,13 @@ p-map@^2.1.0: resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + p-pipe@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/p-pipe/-/p-pipe-1.2.0.tgz#4b1a11399a11520a67790ee5a0c1d5881d6befe9" @@ -11098,6 +11736,14 @@ path-parse@^1.0.6: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-scurry@^1.10.2: + version "1.10.2" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.2.tgz#8f6357eb1239d5fa1da8b9f70e9c080675458ba7" + integrity sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-starts-with@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/path-starts-with/-/path-starts-with-2.0.0.tgz#ffd6d51926cd497022b44d392196033d5451892f" @@ -11338,6 +11984,11 @@ proxy-addr@~2.0.5: forwarded "0.2.0" ipaddr.js "1.9.1" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" @@ -11659,7 +12310,7 @@ read@1, read@~1.0.1: string_decoder "~1.1.1" util-deprecate "~1.0.1" -"readable-stream@2 || 3", readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.0.6, readable-stream@^3.1.0, readable-stream@^3.4.0, readable-stream@^3.6.0: +"readable-stream@2 || 3", readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.0.6, readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -11861,7 +12512,7 @@ repeat-element@^1.1.2: resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== -repeat-string@^1.5.2, repeat-string@^1.6.1: +repeat-string@^1.0.0, repeat-string@^1.5.2, repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= @@ -12204,11 +12855,6 @@ seek-bzip@^1.0.5: dependencies: commander "^2.8.1" -semaphore-async-await@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/semaphore-async-await/-/semaphore-async-await-1.5.1.tgz#857bef5e3644601ca4b9570b87e9df5ca12974fa" - integrity sha1-hXvvXjZEYBykuVcLh+nfXKEpdPo= - semaphore@>=1.0.1, semaphore@^1.0.3, semaphore@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/semaphore/-/semaphore-1.1.0.tgz#aaad8b86b20fe8e9b32b16dc2ee682a8cd26a8aa" @@ -12260,6 +12906,13 @@ send@0.17.1: range-parser "~1.2.1" statuses "~1.5.0" +serialize-javascript@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" + integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== + dependencies: + randombytes "^2.1.0" + serve-static@1.14.1: version "1.14.1" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" @@ -12386,6 +13039,11 @@ signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.5.tgz#9e3e8cc0c75a99472b44321033a7702e7738252f" integrity sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ== +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + simple-concat@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" @@ -12544,6 +13202,31 @@ solidity-coverage@^0.7.9: shelljs "^0.8.3" web3-utils "^1.3.0" +solidity-coverage@^0.8.0: + version "0.8.12" + resolved "https://registry.yarnpkg.com/solidity-coverage/-/solidity-coverage-0.8.12.tgz#c4fa2f64eff8ada7a1387b235d6b5b0e6c6985ed" + integrity sha512-8cOB1PtjnjFRqOgwFiD8DaUsYJtVJ6+YdXQtSZDrLGf8cdhhh8xzTtGzVTGeBf15kTv0v7lYPJlV/az7zLEPJw== + dependencies: + "@ethersproject/abi" "^5.0.9" + "@solidity-parser/parser" "^0.18.0" + chalk "^2.4.2" + death "^1.1.0" + difflib "^0.2.4" + fs-extra "^8.1.0" + ghost-testrpc "^0.0.2" + global-modules "^2.0.0" + globby "^10.0.1" + jsonschema "^1.2.4" + lodash "^4.17.21" + mocha "^10.2.0" + node-emoji "^1.10.0" + pify "^4.0.1" + recursive-readdir "^2.2.2" + sc-istanbul "^0.4.5" + semver "^7.3.4" + shelljs "^0.8.3" + web3-utils "^1.3.6" + solium-plugin-security@0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/solium-plugin-security/-/solium-plugin-security-0.1.1.tgz#2a87bcf8f8c3abf7d198e292e4ac080284e3f3f6" @@ -12808,6 +13491,15 @@ strict-uri-encode@^2.0.0: resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY= +"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -12825,15 +13517,6 @@ string-width@^1.0.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^3.0.0, string-width@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" @@ -12843,6 +13526,15 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + string.prototype.trim@~1.2.4: version "1.2.5" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.5.tgz#a587bcc8bfad8cb9829a577f5de30dd170c1682c" @@ -12887,6 +13579,13 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" @@ -12908,12 +13607,12 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== dependencies: - ansi-regex "^5.0.1" + ansi-regex "^6.0.1" strip-bom@^2.0.0: version "2.0.0" @@ -12975,6 +13674,11 @@ strip-json-comments@2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= +strip-json-comments@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + strip-outer@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/strip-outer/-/strip-outer-1.0.1.tgz#b2fd2abf6604b9d1e6013057195df836b8a9d631" @@ -13010,6 +13714,13 @@ supports-color@6.0.0: dependencies: has-flag "^3.0.0" +supports-color@8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" @@ -13367,11 +14078,6 @@ trim-right@^1.0.1: resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= -"true-case-path@^2.2.1": - version "2.2.1" - resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-2.2.1.tgz#c5bf04a5bbec3fd118be4084461b3a27c4d796bf" - integrity sha512-0z3j8R7MCjy10kc/g+qg7Ln3alJTodw9aDuVWZa3uiWqfuBMKeAeP2ocWcxoyM3D73yz3Jt/Pu4qPr4wHSdB/Q== - truffle-flattener@^1.2.9: version "1.5.0" resolved "https://registry.yarnpkg.com/truffle-flattener/-/truffle-flattener-1.5.0.tgz#c358fa3e5cb0a663429f7912a58c4f0879414e64" @@ -13415,7 +14121,7 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" -tweetnacl-util@^0.15.0: +tweetnacl-util@^0.15.0, tweetnacl-util@^0.15.1: version "0.15.1" resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b" integrity sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw== @@ -13447,6 +14153,11 @@ type-fest@^0.18.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + type-fest@^0.21.3: version "0.21.3" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" @@ -13735,7 +14446,7 @@ util-promisify@^2.1.0: dependencies: object.getownpropertydescriptors "^2.0.3" -util.promisify@^1.0.0, util.promisify@^1.0.1: +util.promisify@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.1.1.tgz#77832f57ced2c9478174149cae9b96e9918cd54b" integrity sha512-/s3UsZUrIfa6xDhr7zZhnE9SLQ5RIXyYfiVnMMyMDzOc8WhWN4Nbh36H842OyurKbCDAesZOJaVyvmSl6fhGQw== @@ -13827,6 +14538,20 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +viem@2.7.14: + version "2.7.14" + resolved "https://registry.yarnpkg.com/viem/-/viem-2.7.14.tgz#347d316cb5400f0b896b2205b1bc8073aa5e27e0" + integrity sha512-5b1KB1gXli02GOQHZIUsRluNUwssl2t4hqdFAzyWPwJ744N83jAOBOjOkrGz7K3qMIv9b0GQt3DoZIErSQTPkQ== + dependencies: + "@adraffy/ens-normalize" "1.10.0" + "@noble/curves" "1.2.0" + "@noble/hashes" "1.3.2" + "@scure/bip32" "1.3.2" + "@scure/bip39" "1.2.1" + abitype "1.0.0" + isows "1.0.3" + ws "8.13.0" + vinyl-fs@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-3.0.3.tgz#c85849405f67428feabbbd5c5dbdd64f47d31bc7" @@ -15045,6 +15770,20 @@ web3-utils@1.7.1: randombytes "^2.1.0" utf8 "3.0.0" +web3-utils@^1.3.6: + version "1.10.4" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.10.4.tgz#0daee7d6841641655d8b3726baf33b08eda1cbec" + integrity sha512-tsu8FiKJLk2PzhDl9fXbGUWTkkVXYhtTA+SmEFkKft+9BgwLxfCRpU96sWv7ICC8zixBNd3JURVoiR3dUXgP8A== + dependencies: + "@ethereumjs/util" "^8.1.0" + bn.js "^5.2.1" + ethereum-bloom-filters "^1.0.6" + ethereum-cryptography "^2.1.2" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + utf8 "3.0.0" + web3@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3/-/web3-1.2.11.tgz#50f458b2e8b11aa37302071c170ed61cff332975" @@ -15222,6 +15961,13 @@ wide-align@^1.1.0: dependencies: string-width "^1.0.2 || 2 || 3 || 4" +widest-line@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" + integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== + dependencies: + string-width "^4.0.0" + windows-release@^3.1.0: version "3.3.3" resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-3.3.3.tgz#1c10027c7225743eec6b89df160d64c2e0293999" @@ -15239,6 +15985,20 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= +workerpool@6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" + integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" @@ -15256,6 +16016,15 @@ wrap-ansi@^5.1.0: string-width "^3.0.0" strip-ansi "^5.0.0" +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -15307,6 +16076,11 @@ ws@7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== +ws@8.13.0: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" + integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== + ws@^3.0.0: version "3.3.3" resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" @@ -15370,7 +16144,7 @@ xmlhttprequest@1.8.0: resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" integrity sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw= -xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.0, xtend@~4.0.1: +xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== @@ -15392,6 +16166,11 @@ y18n@^3.2.1: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + yaeti@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" @@ -15420,6 +16199,11 @@ yargs-parser@13.1.2, yargs-parser@^13.1.0, yargs-parser@^13.1.2: camelcase "^5.0.0" decamelize "^1.2.0" +yargs-parser@20.2.4: + version "20.2.4" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== + yargs-parser@^11.1.1: version "11.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4" @@ -15436,7 +16220,7 @@ yargs-parser@^15.0.1: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^20.2.3: +yargs-parser@^20.2.2, yargs-parser@^20.2.3: version "20.2.9" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== @@ -15457,6 +16241,16 @@ yargs-unparser@1.6.0: lodash "^4.17.15" yargs "^13.3.0" +yargs-unparser@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" + integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== + dependencies: + camelcase "^6.0.0" + decamelize "^4.0.0" + flat "^5.0.2" + is-plain-obj "^2.1.0" + yargs@13.2.4: version "13.2.4" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.2.4.tgz#0b562b794016eb9651b98bd37acf364aa5d6dc83" @@ -15490,6 +16284,19 @@ yargs@13.3.2, yargs@^13.3.0: y18n "^4.0.0" yargs-parser "^13.1.2" +yargs@16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + yargs@^10.0.3: version "10.1.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-10.1.2.tgz#454d074c2b16a51a43e2fb7807e4f9de69ccb5c5" @@ -15550,3 +16357,8 @@ yauzl@^2.4.2: dependencies: buffer-crc32 "~0.2.3" fd-slicer "~1.1.0" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From 8c40de13648411591e281a0311aef8750aeea383 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 18 Jun 2024 11:02:09 +0200 Subject: [PATCH 078/100] fix: getDelegatedVoters condition optimization --- apps/voting/contracts/Voting.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 423528f22..6e1cda0f6 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -472,7 +472,7 @@ contract Voting is IForwarder, AragonApp { address[] storage votersList = delegatedVoters[_delegate].addresses; uint256 votersCount = votersList.length; - if (_offset >= votersCount) { + if (_offset >= votersCount || votersCount == 0) { return voters; } From 5f86b98b6a46e0803b3a07b6263cf5f8c5c88224 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 18 Jun 2024 17:20:49 +0200 Subject: [PATCH 079/100] refactor: rename getVoterStateMultiple to getVoterStateMultipleAtVote --- apps/voting/contracts/Voting.sol | 2 +- apps/voting/test/delegation.js | 56 ++++++++++++++++---------------- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 6e1cda0f6..febfbe88c 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -508,7 +508,7 @@ contract Voting is IForwarder, AragonApp { * @param _voters List of voters * @return Array of voter states */ - function getVoterStateMultiple(uint256 _voteId, address[] _voters) external view voteExists(_voteId) returns (VoterState[] memory voterStatesList) { + function getVoterStateMultipleAtVote(uint256 _voteId, address[] _voters) external view voteExists(_voteId) returns (VoterState[] memory voterStatesList) { uint256 votersCount = _voters.length; voterStatesList = new VoterState[](votersCount); Vote storage vote_ = votes[_voteId]; diff --git a/apps/voting/test/delegation.js b/apps/voting/test/delegation.js index b6a7e3bdc..2b3bdecaa 100644 --- a/apps/voting/test/delegation.js +++ b/apps/voting/test/delegation.js @@ -306,7 +306,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 } await verifyVoteYN(voteId, 0, LDO1.add(LDO20)) - const voterState = await voting.getVoterStateMultiple(voteId, delegatedVoters) + const voterState = await voting.getVoterStateMultipleAtVote(voteId, delegatedVoters) assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) }) @@ -329,7 +329,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 await attemptVoteForMultiple(voteId, true, delegatedVoters, delegate2) await verifyVoteYN(voteId, LDO51.add(LDO29), 0) - const voterState = await voting.getVoterStateMultiple(voteId, delegatedVoters) + const voterState = await voting.getVoterStateMultipleAtVote(voteId, delegatedVoters) assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) }) @@ -351,8 +351,8 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 await verifyVoteYN(voteId, 0, LDO1) - const voterStateHolder1 = await voting.getVoterStateMultiple(voteId, [holder1]) - const voterStateHolder20 = await voting.getVoterStateMultiple(voteId, [holder20]) + const voterStateHolder1 = await voting.getVoterStateMultipleAtVote(voteId, [holder1]) + const voterStateHolder20 = await voting.getVoterStateMultipleAtVote(voteId, [holder20]) assertArraysEqualAsSets(voterStateHolder1, [VOTER_STATE.DELEGATE_NAY.toString()]) assertArraysEqualAsSets(voterStateHolder20, [VOTER_STATE.ABSENT.toString()]) @@ -376,8 +376,8 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 await verifyVoteYN(voteId, LDO29, 0) - const voterStateHolder29 = await voting.getVoterStateMultiple(voteId, [holder29]) - const voterStateHolder51 = await voting.getVoterStateMultiple(voteId, [holder51]) + const voterStateHolder29 = await voting.getVoterStateMultipleAtVote(voteId, [holder29]) + const voterStateHolder51 = await voting.getVoterStateMultipleAtVote(voteId, [holder51]) assertArraysEqualAsSets(voterStateHolder29, [VOTER_STATE.DELEGATE_YEA.toString()]) assertArraysEqualAsSets(voterStateHolder51, [VOTER_STATE.ABSENT.toString()]) @@ -397,14 +397,14 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const supports = false await attemptVoteFor(voteId, supports, holder, delegate1) - const delegatedVoterState = await voting.getVoterStateMultiple(voteId, [holder]) + const delegatedVoterState = await voting.getVoterStateMultipleAtVote(voteId, [holder]) assertArraysEqualAsSets(delegatedVoterState, [VOTER_STATE.DELEGATE_NAY.toString()]) await vote( voteId, !supports, false, holder) await verifyVoteYN(voteId, LDO1, 0) - const voterState = await voting.getVoterStateMultiple(voteId, [holder]) + const voterState = await voting.getVoterStateMultipleAtVote(voteId, [holder]) assertArraysEqualAsSets(voterState, [VOTER_STATE.YEA.toString()]) }) @@ -421,14 +421,14 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 const supports = true await attemptVoteForMultiple(voteId, supports, [holder], delegate2) - const delegatedVoterState = await voting.getVoterStateMultiple(voteId, [holder]) + const delegatedVoterState = await voting.getVoterStateMultipleAtVote(voteId, [holder]) assertArraysEqualAsSets(delegatedVoterState, [VOTER_STATE.DELEGATE_YEA.toString()]) await vote(voteId, !supports, false, holder) await verifyVoteYN(voteId, 0 , LDO29) - const voterState = await voting.getVoterStateMultiple(voteId, [holder]) + const voterState = await voting.getVoterStateMultipleAtVote(voteId, [holder]) assertArraysEqualAsSets(voterState, [VOTER_STATE.NAY.toString()]) }) @@ -451,7 +451,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 await verifyVoteYN(voteId, 0, LDO1) - const voterState = await voting.getVoterStateMultiple(voteId, [holder]) + const voterState = await voting.getVoterStateMultipleAtVote(voteId, [holder]) assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) }) @@ -474,7 +474,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 await verifyVoteYN(voteId, LDO29, 0) - const voterState = await voting.getVoterStateMultiple(voteId, [holder]) + const voterState = await voting.getVoterStateMultipleAtVote(voteId, [holder]) assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) }) @@ -535,7 +535,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 } await verifyVoteYN(voteId, 0, LDO1.add(LDO20)) - const voterState = await voting.getVoterStateMultiple(voteId, delegatedVoters) + const voterState = await voting.getVoterStateMultipleAtVote(voteId, delegatedVoters) assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) }) @@ -558,7 +558,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 await attemptVoteForMultiple(voteId, true, delegatedVoters, delegate2) await verifyVoteYN(voteId, LDO51.add(LDO29), 0) - const voterState = await voting.getVoterStateMultiple(voteId, delegatedVoters) + const voterState = await voting.getVoterStateMultipleAtVote(voteId, delegatedVoters) assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) }) @@ -587,7 +587,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 } await verifyVoteYN(voteId, LDO1.add(LDO20), 0) - const voterState = await voting.getVoterStateMultiple(voteId, delegatedVoters) + const voterState = await voting.getVoterStateMultipleAtVote(voteId, delegatedVoters) assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) }) @@ -611,7 +611,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 await attemptVoteForMultiple(voteId, false, delegatedVoters, delegate2) await verifyVoteYN(voteId, 0, LDO51.add(LDO29)) - const voterState = await voting.getVoterStateMultiple(voteId, delegatedVoters) + const voterState = await voting.getVoterStateMultipleAtVote(voteId, delegatedVoters) assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) }) @@ -644,7 +644,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 } await verifyVoteYN(voteId, 0, LDO1.add(LDO20)) - const voterState = await voting.getVoterStateMultiple(voteId, delegatedVoters) + const voterState = await voting.getVoterStateMultipleAtVote(voteId, delegatedVoters) assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) }) @@ -673,7 +673,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 await verifyVoteYN(voteId, 0, LDO51.add(LDO29)) - const voterState = await voting.getVoterStateMultiple(voteId, delegatedVoters) + const voterState = await voting.getVoterStateMultipleAtVote(voteId, delegatedVoters) assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) }) @@ -712,7 +712,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 } await verifyVoteYN(voteId, 0, LDO1.add(LDO20)) - const voterState = await voting.getVoterStateMultiple(voteId, delegatedVoters) + const voterState = await voting.getVoterStateMultipleAtVote(voteId, delegatedVoters) assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) }) @@ -747,7 +747,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 await verifyVoteYN(voteId, 0, LDO51.add(LDO29)) - const voterState = await voting.getVoterStateMultiple(voteId, delegatedVoters) + const voterState = await voting.getVoterStateMultipleAtVote(voteId, delegatedVoters) assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) }) @@ -780,7 +780,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 } await verifyVoteYN(voteId, 0, LDO1.add(LDO20)) - const voterState = await voting.getVoterStateMultiple(voteId, delegatedVoters) + const voterState = await voting.getVoterStateMultipleAtVote(voteId, delegatedVoters) assertArraysEqualAsSets(voterState, [VOTER_STATE.NAY.toString()]) }) @@ -810,7 +810,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 } await verifyVoteYN(voteId, 0, LDO51.add(LDO29)) - const voterState = await voting.getVoterStateMultiple(voteId, delegatedVoters) + const voterState = await voting.getVoterStateMultipleAtVote(voteId, delegatedVoters) assertArraysEqualAsSets(voterState, [VOTER_STATE.NAY.toString()]) }) @@ -833,10 +833,10 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 await verifyVoteYN(voteId, LDO51.add(LDO29), 0) - const voterState = await voting.getVoterStateMultiple(voteId, [holder29, holder51]) + const voterState = await voting.getVoterStateMultipleAtVote(voteId, [holder29, holder51]) assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) - const voterStateSpam = await voting.getVoterStateMultiple(voteId, spamHolders) + const voterStateSpam = await voting.getVoterStateMultipleAtVote(voteId, spamHolders) assertArraysEqualAsSets(voterStateSpam, [VOTER_STATE.ABSENT.toString()]) }).timeout(60_000); @@ -1042,13 +1042,13 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 metadata = getEventArgument(receipt, 'StartVote', 'metadata') }) - it(`getVoterStateMultiple`, async () => { + it(`getVoterStateMultipleAtVote`, async () => { await voting.vote(voteId, true, false, { from: holder20 }) await voting.vote(voteId, false, false, { from: holder29 }) await voting.attemptVoteForMultiple(voteId, false, [holder51], {from: delegate1}) - await assertRevert(voting.getVoterStateMultiple(voteId + 1, [holder51]), ERRORS.VOTING_NO_VOTE) - const votersState = await voting.getVoterStateMultiple(voteId, [holder20, holder29, holder51]) + await assertRevert(voting.getVoterStateMultipleAtVote(voteId + 1, [holder51]), ERRORS.VOTING_NO_VOTE) + const votersState = await voting.getVoterStateMultipleAtVote(voteId, [holder20, holder29, holder51]) assert.equal(votersState[0], VOTER_STATE.YEA, `holder20 should have 'yea' state`) assert.equal(votersState[1], VOTER_STATE.NAY, `holder29 should have 'nay' state`) assert.equal(votersState[2], VOTER_STATE.DELEGATE_NAY, `holder51 should have 'delegateNay' state`) @@ -1194,7 +1194,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 assertArraysEqualAsSets(filteredDelegatedVoters, votersFromEvent) // Check voters' state - const votersState = await voting.getVoterStateMultiple(voteId, filteredDelegatedVotersAddresses) + const votersState = await voting.getVoterStateMultipleAtVote(voteId, filteredDelegatedVotersAddresses) votersState.every((state) => { assert.equal(state, VOTER_STATE.DELEGATE_NAY.toString(), `voter should have 'delegateNay' state`) }) From 53b327c398ecc80bdb81d27cb648102e4eef224f Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 18 Jun 2024 18:39:58 +0200 Subject: [PATCH 080/100] docs: add dev comment to attemptVoteForMultiple --- apps/voting/contracts/Voting.sol | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index febfbe88c..e902c65a7 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -23,8 +23,8 @@ contract Voting is IForwarder, AragonApp { bytes32 public constant UNSAFELY_MODIFY_VOTE_TIME_ROLE = keccak256("UNSAFELY_MODIFY_VOTE_TIME_ROLE"); uint64 public constant PCT_BASE = 10 ** 18; // 0% = 0; 1% = 10^16; 100% = 10^18 - uint256 private constant UINT_96_MAX = 2 ** 96 - 1; + string private constant ERROR_NO_VOTE = "VOTING_NO_VOTE"; string private constant ERROR_INIT_PCTS = "VOTING_INIT_PCTS"; string private constant ERROR_CHANGE_SUPPORT_PCTS = "VOTING_CHANGE_SUPPORT_PCTS"; @@ -277,6 +277,11 @@ contract Voting is IForwarder, AragonApp { /** * @notice Vote `_supports ? 'yes' : 'no'` in vote #`_voteId` on behalf of the `_voters` list. * Each voter from the list must have assigned the sender as their delegate with `assignDelegate`. + * @dev By the delegation mechanism design, it is possible for a misbehaving voter to front-run a delegate by voting + * or unassigning the delegation before the delegate's vote. That is why checks of the address belonging to + * the list of delegated voters and the voter's state are unstrict, hence the "attempt" word in the function name. + * On the other hand, the voter's voting power is being checked at the vote's snapshot block, making a potential + * manipulation by moving voting power from one address to another worthless. * @param _voteId Vote identifier * @param _supports Whether the delegate supports the vote * @param _voters List of voters From 19f14bcc176a3d8c305cdffa708e19569110e683 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 25 Jun 2024 20:01:25 +0200 Subject: [PATCH 081/100] docs: update comments and docs --- apps/voting/contracts/Voting.sol | 34 ++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index e902c65a7..cb0552449 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -265,7 +265,7 @@ contract Voting is IForwarder, AragonApp { } /** - * @notice Unassign `_delegate` from the sender and remove the sender from the `_delegate's` list of delegated addresses + * @notice Unassign `_delegate` from the sender and remove the sender from the `_delegate`'s list of delegated addresses */ function unassignDelegate() external { address prevDelegate = delegates[msg.sender].delegate; @@ -278,10 +278,10 @@ contract Voting is IForwarder, AragonApp { * @notice Vote `_supports ? 'yes' : 'no'` in vote #`_voteId` on behalf of the `_voters` list. * Each voter from the list must have assigned the sender as their delegate with `assignDelegate`. * @dev By the delegation mechanism design, it is possible for a misbehaving voter to front-run a delegate by voting - * or unassigning the delegation before the delegate's vote. That is why checks of the address belonging to - * the list of delegated voters and the voter's state are unstrict, hence the "attempt" word in the function name. - * On the other hand, the voter's voting power is being checked at the vote's snapshot block, making a potential - * manipulation by moving voting power from one address to another worthless. + * or unassigning the delegation before the delegate's vote. That is why checks of the address belonging to + * the list of delegated voters and the voter's state are unstrict, hence the "attempt" word in the function name. + * The voter's voting power is being fetched at the vote's snapshot block, making moving voting power + * from one address to another worthless in the sense of harming the delegate's actions during the vote. * @param _voteId Vote identifier * @param _supports Whether the delegate supports the vote * @param _voters List of voters @@ -388,7 +388,8 @@ contract Voting is IForwarder, AragonApp { * created via `newVote(),` which requires initialization * @param _voteId Vote identifier * @param _voter Address of the voter - * @return True if the given voter can participate in the main phase of a certain vote, false otherwise + * @return True if the given voter can participate in the main phase of a certain vote + * both directly and indirectly by a delegate voting for them, false otherwise */ function canVote(uint256 _voteId, address _voter) external view voteExists(_voteId) returns (bool) { Vote storage vote_ = votes[_voteId]; @@ -466,10 +467,10 @@ contract Voting is IForwarder, AragonApp { } /** - * @notice Return the sliced list of delegated voters for `_delegate` + * @notice Return the sliced list of voters who delegated to the `_delegate` * @param _delegate Address of the delegate * @param _offset Number of delegated voters from the start of the list to skip - * @param _limit Number of delegated voters to return + * @param _limit Maximum number of delegated voters to return. The length of the returned slice can be lower than if the end of the list is reached * @return Array of delegated voters' addresses */ function getDelegatedVoters(address _delegate, uint256 _offset, uint256 _limit) external view returns (address[] memory voters) { @@ -489,7 +490,7 @@ contract Voting is IForwarder, AragonApp { } /** - * @dev Return the number of delegated voters for the `_delegate` + * @dev Return the number of voters who delegated to the `_delegate` * @param _delegate Address of the delegate * @return Number of `_delegate`'s delegated voters */ @@ -499,7 +500,7 @@ contract Voting is IForwarder, AragonApp { } /** - * @notice Return the delegate address assigned to the `_voter` + * @notice Return the delegate address of the `_voter` * @param _voter Address of the voter */ function getDelegate(address _voter) external view returns (address) { @@ -523,7 +524,7 @@ contract Voting is IForwarder, AragonApp { } /** - * @notice Return the voting power of the `_voters` at the current block + * @notice Return the cumulative voting power of the `_voters` at the current block * @param _voters List of voters * @return Array of governance token balances */ @@ -532,7 +533,7 @@ contract Voting is IForwarder, AragonApp { } /** - * @notice Return the voting power of the `_voters` at the vote #`_voteId` snapshot block + * @notice Return the cumulative voting power of the `_voters` at the vote #`_voteId` snapshot block * @param _voteId Vote identifier * @param _voters List of voters * @return Array of governance token balances @@ -667,7 +668,7 @@ contract Voting is IForwarder, AragonApp { } /** - * @dev Internal function to get the voting power of the `_voters` at the `_blockNumber` + * @dev Internal function to get the cumulative voting power of the `_voters` at the `_blockNumber` * @param _voters List of voters * @param _blockNumber Target block number */ @@ -722,7 +723,9 @@ contract Voting is IForwarder, AragonApp { } /** - * @dev Internal function to check if the _delegate can vote on behalf of the _voter in the given vote + * @dev Internal function to check if the _delegate can vote on behalf of the _voter in the given vote. + * Note that the overpowering is possible both by the delegate's voter themself + * and by a new delegate they might elect while the vote is running. * @param vote_ The queried vote * @param _delegate Address of the delegate * @param _voter Address of the voter @@ -737,7 +740,8 @@ contract Voting is IForwarder, AragonApp { if (delegates[_voter].delegate != _delegate) { return false; } - // Otherwise, the _voter must not have voted directly + + // The _voter must not have voted directly VoterState state = vote_.voters[_voter]; return state != VoterState.Yea && state != VoterState.Nay; } From 456cb54b903a74983c6c50398ba743c0b50d9d10 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 25 Jun 2024 20:04:05 +0200 Subject: [PATCH 082/100] fix: move snapshotBlock getter out of the loop --- apps/voting/contracts/Voting.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index cb0552449..fe4e6d988 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -293,13 +293,14 @@ contract Voting is IForwarder, AragonApp { require(_isValidPhaseToVote(votePhase, _supports), ERROR_CAN_NOT_VOTE); bool votedForAtLeastOne = false; + uint64 voteSnapshotBlock = vote_.snapshotBlock; for (uint256 i = 0; i < _voters.length; ++i) { address voter = _voters[i]; // In version 0.4.24 of Solidity, for view methods, a regular CALL is used instead of STATICCALL. // This allows for the possibility of reentrance from the governance token. // However, we strongly assume here that the governance token is not malicious. - uint256 votingPower = token.balanceOfAt(voter, vote_.snapshotBlock); + uint256 votingPower = token.balanceOfAt(voter, voteSnapshotBlock); // A strict check for balance was introduced to have a persistent logic with the `vote` method. // It's not possible to front-run the voting attempt with balance manipulation since the balances are being checked at the vote's snapshot block From 375573c0425ec4a2f121842ea7287cb8830d2d8e Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 25 Jun 2024 20:32:50 +0200 Subject: [PATCH 083/100] refactor: replace _isVoteOpen with a comprehensive version of _getVotePhase and _isValidPhaseToVote --- apps/voting/contracts/Voting.sol | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index fe4e6d988..7a6bbdc3f 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -225,7 +225,6 @@ contract Voting is IForwarder, AragonApp { */ function vote(uint256 _voteId, bool _supports, bool /* _executesIfDecided_deprecated */) external voteExists(_voteId) { Vote storage vote_ = votes[_voteId]; - require(_isVoteOpen(vote_), ERROR_CAN_NOT_VOTE); VotePhase votePhase = _getVotePhase(vote_); require(_isValidPhaseToVote(votePhase, _supports), ERROR_CAN_NOT_VOTE); @@ -288,7 +287,6 @@ contract Voting is IForwarder, AragonApp { */ function attemptVoteForMultiple(uint256 _voteId, bool _supports, address[] _voters) public voteExists(_voteId) { Vote storage vote_ = votes[_voteId]; - require(_isVoteOpen(vote_), ERROR_CAN_NOT_VOTE); VotePhase votePhase = _getVotePhase(vote_); require(_isValidPhaseToVote(votePhase, _supports), ERROR_CAN_NOT_VOTE); @@ -395,7 +393,7 @@ contract Voting is IForwarder, AragonApp { function canVote(uint256 _voteId, address _voter) external view voteExists(_voteId) returns (bool) { Vote storage vote_ = votes[_voteId]; uint256 votingPower = token.balanceOfAt(_voter, vote_.snapshotBlock); - return _isVoteOpen(vote_) && votingPower > 0; + return _getVotePhase(vote_) != VotePhase.Closed && votingPower > 0; } /** @@ -444,7 +442,6 @@ contract Voting is IForwarder, AragonApp { { Vote storage vote_ = votes[_voteId]; - open = _isVoteOpen(vote_); executed = vote_.executed; startDate = vote_.startDate; snapshotBlock = vote_.snapshotBlock; @@ -455,6 +452,7 @@ contract Voting is IForwarder, AragonApp { votingPower = vote_.votingPower; script = vote_.executionScript; phase = _getVotePhase(vote_); + open = phase != VotePhase.Closed; } /** @@ -694,7 +692,7 @@ contract Voting is IForwarder, AragonApp { } // Vote ended? - if (_isVoteOpen(vote_)) { + if (_getVotePhase(vote_) != VotePhase.Closed) { return false; } @@ -720,7 +718,16 @@ contract Voting is IForwarder, AragonApp { * @return True if the given voter can participate a certain vote, false otherwise */ function _isValidPhaseToVote(VotePhase _votePhase, bool _supports) internal pure returns (bool) { - return !_supports || _votePhase == VotePhase.Main; + // In the Main phase both Yea and Nay votes are allowed + if (_votePhase == VotePhase.Main) { + return true; + } + // During the Objection phase only Nay votes are allowed + if (_votePhase == VotePhase.Objection) { + return !_supports; + } + // It's not allowed to vote in any other cases + return false; } /** @@ -755,13 +762,16 @@ contract Voting is IForwarder, AragonApp { function _getVotePhase(Vote storage vote_) internal view returns (VotePhase) { uint64 timestamp = getTimestamp64(); uint64 voteTimeEnd = vote_.startDate.add(voteTime); + if (timestamp >= voteTimeEnd || vote_.executed) { + return VotePhase.Closed; + } if (timestamp < voteTimeEnd.sub(objectionPhaseTime)) { return VotePhase.Main; } if (timestamp < voteTimeEnd) { return VotePhase.Objection; } - return VotePhase.Closed; + assert(false); // Should never reach this point } /** From f1283de2e7e8dc8df825ce637d0ee8614cd68c53 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 25 Jun 2024 20:46:54 +0200 Subject: [PATCH 084/100] docs: add VoterState values descriptions --- apps/voting/contracts/Voting.sol | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 7a6bbdc3f..cdf701a80 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -45,7 +45,13 @@ contract Voting is IForwarder, AragonApp { string private constant ERROR_DELEGATE_SAME_AS_PREV = "VOTING_DELEGATE_SAME_AS_PREV"; string private constant ERROR_MAX_DELEGATED_VOTERS_REACHED = "VOTING_MAX_DELEGATED_VOTERS_REACHED"; - enum VoterState { Absent, Yea, Nay, DelegateYea, DelegateNay } + enum VoterState { + Absent, // Voter has not voted + Yea, // Voter has voted for + Nay, // Voter has voted against + DelegateYea, // Delegate has voted for on behalf of the voter + DelegateNay // Delegate has voted against on behalf of the voter + } enum VotePhase { Main, Objection, Closed } From 079dd88e1e6e55f223792d1becb3fe68427473e5 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 25 Jun 2024 20:50:48 +0200 Subject: [PATCH 085/100] docs: fix comment --- apps/voting/contracts/Voting.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index cdf701a80..4229093b2 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -717,7 +717,7 @@ contract Voting is IForwarder, AragonApp { } /** - * @dev Internal function to check if the vote is open and given option is applicable at the current phase. + * @dev Internal function to check if the given option is applicable at the given vote phase. * It assumes the queried vote exists. * @param _votePhase The queried vote phase * @param _supports Whether the voter supports the vote From feb1515d31f18587d1bf96cca358db1d9911398f Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Thu, 4 Jul 2024 00:40:17 +0200 Subject: [PATCH 086/100] test: rework delegation tests --- apps/voting/test/delegation.js | 1512 +++++++---------- apps/voting/test/helpers/assertArrayAsSets.js | 6 +- 2 files changed, 570 insertions(+), 948 deletions(-) diff --git a/apps/voting/test/delegation.js b/apps/voting/test/delegation.js index 2b3bdecaa..0fbbca818 100644 --- a/apps/voting/test/delegation.js +++ b/apps/voting/test/delegation.js @@ -1,33 +1,59 @@ const ERRORS = require('./helpers/errors') const assertArraysEqualAsSets = require('./helpers/assertArrayAsSets') const { assertBn, assertRevert, assertAmountOfEvents, assertEvent } = require('@aragon/contract-helpers-test/src/asserts') -const { pct16, bn, bigExp, getEventArgument, ZERO_ADDRESS } = require('@aragon/contract-helpers-test') -const { newDao, installNewApp, encodeCallScript, ANY_ENTITY, EMPTY_CALLS_SCRIPT } = require('@aragon/contract-helpers-test/src/aragon-os') +const { pct16, bigExp, getEventArgument, ZERO_ADDRESS } = require('@aragon/contract-helpers-test') +const { newDao, installNewApp, ANY_ENTITY, EMPTY_CALLS_SCRIPT } = require('@aragon/contract-helpers-test/src/aragon-os') const { assert } = require('chai') const { getStorageAt, setStorageAt, impersonateAccount } = require("@nomicfoundation/hardhat-network-helpers") const Voting = artifacts.require('VotingMock') - const MiniMeToken = artifacts.require('MiniMeToken') -const ExecutionTarget = artifacts.require('ExecutionTarget') - -const createdVoteId = receipt => getEventArgument(receipt, 'StartVote', 'voteId') -const VOTER_STATE = ['ABSENT', 'YEA', 'NAY', 'DELEGATE_YEA', 'DELEGATE_NAY'].reduce((state, key, index) => { - state[key] = index; - return state; -}, {}) +// Voting contract config +const NEEDED_SUPPORT = pct16(50) +const MIN_ACCEPTANCE_QUORUM = pct16(20) +const NOW = 1 +const MAIN_PHASE_DURATION = 700 +const OBJECTION_PHASE_DURATION = 300 +const VOTING_DURATION = MAIN_PHASE_DURATION + OBJECTION_PHASE_DURATION +const APP_ID = '0x1234123412341234123412341234123412341234123412341234123412341234' + +// Voting contract state constants +const VOTER_STATE = { + ABSENT: "0", + YEA: "1", + NAY: "2", + DELEGATE_YEA: "3", + DELEGATE_NAY: "4" +} +const VOTE_PHASE = { + MAIN: "0", + OBJECTION: "1" +} +const VOTE_YEA_VALUE = true +const VOTE_NAY_VALUE = false + +// Voting token parameter +const TOKEN_DECIMALS = 18 + +const ZERO_BN = bigExp(0, TOKEN_DECIMALS) + +const getVoteIdFromReceipt = receipt => getEventArgument(receipt, 'StartVote', 'voteId') +const getVotingPowerSum = (balances) => balances.reduce((sum, balance) => sum.add(balance), ZERO_BN) contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29, holder51, delegate1, delegate2, nonHolder, ...spamHolders]) => { - let votingBase, voting, token, executionTarget, aclP + let votingBase, voting, token, voteId let CREATE_VOTES_ROLE, MODIFY_SUPPORT_ROLE, MODIFY_QUORUM_ROLE, UNSAFELY_MODIFY_VOTE_TIME_ROLE - const NOW = 1 - const mainPhase = 700 - const objectionPhase = 300 - const votingDuration = mainPhase + objectionPhase - - const APP_ID = '0x1234123412341234123412341234123412341234123412341234123412341234' + const voters = [holder1, holder2, holder20, holder29, holder51] + const votersBalances = { + [holder1]: bigExp(1, TOKEN_DECIMALS), + [holder2]: bigExp(2, TOKEN_DECIMALS), + [holder20]: bigExp(20, TOKEN_DECIMALS), + [holder29]: bigExp(29, TOKEN_DECIMALS), + [holder51]: bigExp(51, TOKEN_DECIMALS), + } + const defaultLimit = 100 // default limit for getDelegatedVoters before('load roles', async () => { votingBase = await Voting.new() @@ -45,139 +71,58 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 await acl.createPermission(ANY_ENTITY, voting.address, MODIFY_SUPPORT_ROLE, root, { from: root }) await acl.createPermission(ANY_ENTITY, voting.address, MODIFY_QUORUM_ROLE, root, { from: root }) await acl.createPermission(ANY_ENTITY, voting.address, UNSAFELY_MODIFY_VOTE_TIME_ROLE, root, { from: root }) - aclP = acl + // Initialize voting contract + token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', TOKEN_DECIMALS, 'n', true) // empty parameters minime + await voting.initialize(token.address, NEEDED_SUPPORT, MIN_ACCEPTANCE_QUORUM, VOTING_DURATION, OBJECTION_PHASE_DURATION) }) - context('simple delegation scenarios', () => { - const neededSupport = pct16(50) - const minimumAcceptanceQuorum = pct16(20) - const decimals = 18 - - const MAIN_PHASE = 0 - const OBJECTION_PHASE = 1 - - const LDO1 = bigExp(1, decimals) - const LDO20 = bigExp(20, decimals) - const LDO29 = bigExp(29, decimals) - const LDO51 = bigExp(51, decimals) - const LDO3 = bigExp(3, decimals) - const initBalance = { - [holder1]: LDO1, - [holder20]: LDO20, - [holder29]: LDO29, - [holder51]: LDO51, - } - - const assignDelegate = async (delegate, holder ) => { - const tx = await voting.assignDelegate(delegate, {from: holder}) - assertEvent(tx, 'AssignDelegate', { - expectedArgs: {voter: holder, assignedDelegate: delegate} - }) - assertAmountOfEvents(tx, 'AssignDelegate', {expectedAmount: 1}) - } - const attemptVoteFor = async (voteId, supports, holder, delegate) => { - const tx = await voting.attemptVoteFor(voteId, supports, holder, {from: delegate}) - assertEvent(tx, 'CastVote', { - expectedArgs: {voteId: voteId, voter: holder, supports, stake: initBalance[holder]} - }) - assertEvent(tx, 'AttemptCastVoteAsDelegate', { expectedArgs: {voteId, delegate} }) - const votersFromEvent = getEventArgument(tx, 'AttemptCastVoteAsDelegate', 'voters') - assertArraysEqualAsSets([holder], votersFromEvent) - assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) - assertAmountOfEvents(tx, 'AttemptCastVoteAsDelegate', {expectedAmount: 1}) - } - const attemptVoteForMultiple = async (voteId, supports, holders, delegate) => { - const tx = await voting.attemptVoteForMultiple(voteId, supports, holders, {from: delegate}) - - for (const index of Object.keys(holders)){ - const holder = holders[index] - let stake - if (initBalance[holder]) { - stake = initBalance[holder] - } - if (!stake && spamHolders.includes(holder)) { - stake = LDO3 - } - assertEvent(tx, 'CastVote', { - index, - expectedArgs: {voteId, voter: holder, supports, stake} - }) - } - assertEvent(tx, 'AttemptCastVoteAsDelegate', { expectedArgs: {voteId, delegate} }) - const votersFromEvent = getEventArgument(tx, 'AttemptCastVoteAsDelegate', 'voters') - assertArraysEqualAsSets(holders, votersFromEvent) - assertAmountOfEvents(tx, 'CastVote', {expectedAmount: holders.length}) - assertAmountOfEvents(tx, 'AttemptCastVoteAsDelegate', {expectedAmount: 1}) - } - const vote = async (voteId, supports, exec, holder) => { - const tx = await voting.vote(voteId, supports, exec, {from: holder}) - assertEvent(tx, 'CastVote', { - expectedArgs: {voteId: voteId, voter: holder, supports, stake: initBalance[holder]} - }) - assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) - } - const verifyVoteYN = async (voteId, yes, no) => { - const { yea, nay } = await voting.getVote(voteId) - - assert.equal(yea.toString(), yes.toString()) - assert.equal(nay.toString(), no.toString()) - } + const startEmptyVote = async () => getVoteIdFromReceipt(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - beforeEach(async () => { - token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', decimals, 'n', true) // empty parameters minime - for (const [holder, balance] of Object.entries(initBalance)){ - await token.generateTokens(holder, balance) - } - - await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, objectionPhase) - - executionTarget = await ExecutionTarget.new() - }) - - it(`voter can't set the zero address as a delegate`, async () => { + context('delegation state management', () => { + it(`voter can't assign the zero address as a delegate`, async () => { await assertRevert( - voting.assignDelegate(ZERO_ADDRESS, {from: holder29}), + voting.assignDelegate(ZERO_ADDRESS, { from: voters[0] }), ERRORS.VOTING_ZERO_ADDRESS_PASSED ) }) it(`voter can't assign themself as a delegate`, async () => { await assertRevert( - voting.assignDelegate(holder29, {from: holder29}), + voting.assignDelegate(voters[0], { from: voters[0] }), ERRORS.VOTING_SELF_DELEGATE ) }) it(`voter can't assign their current delegate as a delegate`, async () => { - await voting.assignDelegate(delegate1, {from: holder29}) + await voting.assignDelegate(delegate1, { from: voters[0] }) await assertRevert( - voting.assignDelegate(delegate1, {from: holder29}), + voting.assignDelegate(delegate1, { from: voters[0] }), ERRORS.VOTING_DELEGATE_SAME_AS_PREV ) }) it(`voter can't unassign their delegate if they wasn't assigned before`, async () => { await assertRevert( - voting.unassignDelegate({from: holder29}), + voting.unassignDelegate({ from: voters[0] }), ERRORS.VOTING_DELEGATE_NOT_SET ) }) - it('voter can set delegate', async () => { - let delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 1) + it('voter can assign a delegate', async () => { + let delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, defaultLimit) assert.equal(delegatedVoters.length, 0, 'delegate1 should not be a delegate of anyone') - const tx = await voting.assignDelegate(delegate1, {from: holder29}) + const tx = await voting.assignDelegate(delegate1, { from: voters[0] }) assertEvent(tx, 'AssignDelegate', { - expectedArgs: {voter: holder29, assignedDelegate: delegate1} + expectedArgs: { voter: voters[0], assignedDelegate: delegate1 } }) - assertAmountOfEvents(tx, 'AssignDelegate', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'AssignDelegate', { expectedAmount: 1 }) - const delegate = await voting.getDelegate(holder29) - assert.equal(delegate, delegate1, 'holder29 should have delegate1 as a delegate') + const delegate = await voting.getDelegate(voters[0]) + assert.equal(delegate, delegate1) - delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 1) - assertArraysEqualAsSets(delegatedVoters, [holder29], 'delegate1 should be a delegate of holder29') + delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, defaultLimit) + assertArraysEqualAsSets(delegatedVoters, [voters[0]]) }) it(`assignment fails if delegatedVoters array is overflown`, async () => { @@ -190,696 +135,104 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 let storage = await getStorageAt(voting.address, arrayLengthSlot) assert(ethers.BigNumber.from(storage).eq(0), 'delegatedVoters array length should be 0') - await voting.assignDelegate(delegate1, {from: holder29}) + await voting.assignDelegate(delegate1, { from: voters[0] }) storage = await getStorageAt(voting.address, arrayLengthSlot) assert(ethers.BigNumber.from(storage).eq(1), 'delegatedVoters array length should be 1 after assignment') - // Update slot value to max uint96 - const uint96Max = ethers.BigNumber.from(2).pow(96) - await setStorageAt(voting.address, arrayLengthSlot, uint96Max) + // Update slot value to max uint96 - 1 + const uint96MaxMinusOne = ethers.BigNumber.from(2).pow(96).sub(1) + await setStorageAt(voting.address, arrayLengthSlot, uint96MaxMinusOne) + // Should successfully assign delegate + const tx = await voting.assignDelegate(delegate1, { from: voters[1] }) + assertEvent(tx, 'AssignDelegate', { + expectedArgs: { voter: voters[1], assignedDelegate: delegate1 } + }) + assertAmountOfEvents(tx, 'AssignDelegate', { expectedAmount: 1 }) - // Check that revert is thrown when trying to assign a delegate + // Check that revert is thrown when trying to assign a delegate if the capacity is reached await assertRevert( - voting.assignDelegate(delegate1, {from: holder51}), + voting.assignDelegate(delegate1, { from: voters[2] }), ERRORS.VOTING_MAX_DELEGATED_VOTERS_REACHED ) }) - it('voter can remove delegate', async () => { - let delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 1) + it('single voter can unassign a delegate', async () => { + let delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, defaultLimit) assert.equal(delegatedVoters.length, 0, 'delegate1 should not be a delegate of anyone') - await voting.assignDelegate(delegate1, {from: holder29}) + await voting.assignDelegate(delegate1, { from: voters[0] }) - delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 1) - assertArraysEqualAsSets(delegatedVoters, [holder29], 'delegate1 should be a delegate of holder29') + delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, defaultLimit) + assertArraysEqualAsSets(delegatedVoters, [voters[0]]) - const tx = await voting.unassignDelegate({from: holder29}) + const tx = await voting.unassignDelegate({ from: voters[0] }) assertEvent(tx, 'UnassignDelegate', { - expectedArgs: {voter: holder29, unassignedDelegate: delegate1} + expectedArgs: { voter: voters[0], unassignedDelegate: delegate1 } }) - assertAmountOfEvents(tx, 'UnassignDelegate', {expectedAmount: 1}) - delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 1) + assertAmountOfEvents(tx, 'UnassignDelegate', { expectedAmount: 1 }) + delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, defaultLimit) assertArraysEqualAsSets(delegatedVoters, [], 'delegate1 should not be a delegate of anyone') }) - it('voters can remove delegate', async () => { - let delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 1) + it('multiple voters can unassign a delegate', async () => { + let delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, defaultLimit) assert.equal(delegatedVoters.length, 0, 'delegate1 should not be a delegate of anyone') - await voting.assignDelegate(delegate1, {from: holder20}) - await voting.assignDelegate(delegate1, {from: holder29}) - await voting.assignDelegate(delegate1, {from: holder51}) + for (const voter of voters) { + await voting.assignDelegate(delegate1, { from: voter }) + } - const tx1 = await voting.unassignDelegate({from: holder29}) + const tx1 = await voting.unassignDelegate({ from: voters[0] }) assertEvent(tx1, 'UnassignDelegate', { - expectedArgs: {voter: holder29, unassignedDelegate: delegate1} + expectedArgs: { voter: voters[0], unassignedDelegate: delegate1 } }) - assertAmountOfEvents(tx1, 'UnassignDelegate', {expectedAmount: 1}) - delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 5) - assertArraysEqualAsSets(delegatedVoters, [holder20, holder51], 'delegate1 have holder20 and holder51 as a delegated voters') + assertAmountOfEvents(tx1, 'UnassignDelegate', { expectedAmount: 1 }) + delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, defaultLimit) + assertArraysEqualAsSets(delegatedVoters, voters.slice(1)) - const tx2 = await voting.unassignDelegate({from: holder51}) + const tx2 = await voting.unassignDelegate({ from: voters[1] }) assertEvent(tx2, 'UnassignDelegate', { - expectedArgs: {voter: holder51, unassignedDelegate: delegate1} + expectedArgs: { voter: voters[1] , unassignedDelegate: delegate1 } }) - assertAmountOfEvents(tx2, 'UnassignDelegate', {expectedAmount: 1}) - delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 1) - assertArraysEqualAsSets(delegatedVoters, [holder20], 'delegate1 have only holder20 as a delegated voter') + assertAmountOfEvents(tx2, 'UnassignDelegate', { expectedAmount: 1 }) + delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, defaultLimit) + assertArraysEqualAsSets(delegatedVoters, voters.slice(2)) }) it('voter can change delegate', async () => { - let delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 1) + let delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, defaultLimit) assert.equal(delegatedVoters.length, 0, 'delegate1 should not be a delegate of anyone') - delegatedVoters = await voting.getDelegatedVoters(delegate2, 0, 1) + delegatedVoters = await voting.getDelegatedVoters(delegate2, 0, defaultLimit) assert.equal(delegatedVoters.length, 0, 'delegate2 should not be a delegate of anyone') - await voting.assignDelegate(delegate1, {from: holder29}) - await voting.assignDelegate(delegate2, {from: holder51}) - - await voting.assignDelegate(delegate2, {from: holder29}) - - const delegatedVotersDelegate1 = await voting.getDelegatedVoters(delegate1, 0, 1) - assertArraysEqualAsSets(delegatedVotersDelegate1, [], 'delegate1 should not be a delegate of anyone') - const delegatedVotersDelegate2 = await voting.getDelegatedVoters(delegate2, 0, 2) - assertArraysEqualAsSets(delegatedVotersDelegate2, [holder29, holder51], 'delegate2 should be a delegate of holder29 and holder51') - }) - - it('delegate can manage several voters', async () => { - let delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 1) - assert.equal(delegatedVoters.length, 0, 'delegate1 should not be a delegate of anyone') - - const tx1 = await voting.assignDelegate(delegate1, {from: holder29}) - assertEvent(tx1, 'AssignDelegate', { - expectedArgs: {voter: holder29, assignedDelegate: delegate1} - }) - assertAmountOfEvents(tx1, 'AssignDelegate', {expectedAmount: 1}) - - const tx2 = await voting.assignDelegate(delegate1, {from: holder51}) - assertEvent(tx2, 'AssignDelegate', { - expectedArgs: {voter: holder51, assignedDelegate: delegate1} - }) - assertAmountOfEvents(tx2, 'AssignDelegate', {expectedAmount: 1}) - - delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 2) - assertArraysEqualAsSets(delegatedVoters, [holder29, holder51], 'delegate1 should be a delegate of holder29 and holder51') - }) - - // Multiple voters with non-zero balances of governance token are delegating their voting - // power to a single delegate. The voting starts and the delegate is voting for all of them - first - // they get the full list of voters with their balance snapshot and vote states of the given voting, - // then they vote for that list. - it('delegate can manage several voters and vote for all (voteFor)', async () => { - const delegateList = [ [delegate1, holder1], [delegate1, holder20] ] - - for (const [delegate, holder] of delegateList) { - await assignDelegate(delegate, holder) - } - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - const delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 3) - assertArraysEqualAsSets(delegatedVoters, [holder1, holder20]) - - for (const holder of delegatedVoters) { - await attemptVoteFor(voteId, false, holder, delegate1) - } - - await verifyVoteYN(voteId, 0, LDO1.add(LDO20)) - const voterState = await voting.getVoterStateMultipleAtVote(voteId, delegatedVoters) - assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) - }) - - // Multiple voters with non-zero balances of governance token are delegating their voting - // power to a single delegate. The voting starts and the delegate is voting for all of them - first - // they get the full list of voters with their balance snapshot and vote states of the given voting, - // then they vote for that list. - it('delegate can manage several voters and vote all (voteForMulti)', async () => { - const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] - for (const [delegate, holder] of delegateList) { - await assignDelegate(delegate, holder) - } - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - const delegatedVoters = await voting.getDelegatedVoters(delegate2, 0, 3) - - assertArraysEqualAsSets(delegatedVoters, [holder29,holder51]) - - await attemptVoteForMultiple(voteId, true, delegatedVoters, delegate2) - await verifyVoteYN(voteId, LDO51.add(LDO29), 0) - - const voterState = await voting.getVoterStateMultipleAtVote(voteId, delegatedVoters) - assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) - }) - - // Multiple voters with non-zero balances of governance token are delegating their voting - // power to a single delegate. The voting starts and the delegate is voting for one of them. - it('delegate can manage several voters and vote for first (voteFor)', async () => { - const delegateList= [ [delegate1, holder1], [delegate1, holder20] ] - - for (const [delegate, holder] of delegateList) { - await assignDelegate(delegate, holder) + for (const voter of voters) { + await voting.assignDelegate(delegate1, { from: voter }) } - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - const delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 3) - assertArraysEqualAsSets(delegatedVoters, [holder1, holder20]) - - await attemptVoteFor(voteId, false, holder1, delegate1) - - await verifyVoteYN(voteId, 0, LDO1) - - const voterStateHolder1 = await voting.getVoterStateMultipleAtVote(voteId, [holder1]) - const voterStateHolder20 = await voting.getVoterStateMultipleAtVote(voteId, [holder20]) + await voting.assignDelegate(delegate2, { from: voters[0] }) - assertArraysEqualAsSets(voterStateHolder1, [VOTER_STATE.DELEGATE_NAY.toString()]) - assertArraysEqualAsSets(voterStateHolder20, [VOTER_STATE.ABSENT.toString()]) + const delegatedVotersDelegate1 = await voting.getDelegatedVoters(delegate1, 0, defaultLimit) + assertArraysEqualAsSets(delegatedVotersDelegate1, voters.slice(1)) + const delegatedVotersDelegate2 = await voting.getDelegatedVoters(delegate2, 0, defaultLimit) + assertArraysEqualAsSets(delegatedVotersDelegate2, [voters[0]]) }) - - // Multiple voters with non-zero balances of governance token are delegating their voting - // power to a single delegate. The voting starts and the delegate is voting for one of them. - it('delegate can manage several voters and vote for first (voteForMulti)', async () => { - const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] - for (const [delegate, holder] of delegateList) { - assignDelegate(delegate, holder) - } - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - const delegatedVoters = await voting.getDelegatedVoters(delegate2, 0, 3) - - assertArraysEqualAsSets(delegatedVoters, [ holder29, holder51 ]) - - await attemptVoteForMultiple(voteId, true, [holder29], delegate2) - - await verifyVoteYN(voteId, LDO29, 0) - - const voterStateHolder29 = await voting.getVoterStateMultipleAtVote(voteId, [holder29]) - const voterStateHolder51 = await voting.getVoterStateMultipleAtVote(voteId, [holder51]) - - assertArraysEqualAsSets(voterStateHolder29, [VOTER_STATE.DELEGATE_YEA.toString()]) - assertArraysEqualAsSets(voterStateHolder51, [VOTER_STATE.ABSENT.toString()]) - }) - - // A delegated voter can overwrite a delegate's vote. - it('delegated voter can overwrite a delegates vote (voteFor)', async () => { - const [ delegate, holder] = [ delegate1, holder1 ] - - assignDelegate(delegate, holder) - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - const delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 3) - assertArraysEqualAsSets(delegatedVoters, [ holder1 ]) - - const supports = false - await attemptVoteFor(voteId, supports, holder, delegate1) - - const delegatedVoterState = await voting.getVoterStateMultipleAtVote(voteId, [holder]) - assertArraysEqualAsSets(delegatedVoterState, [VOTER_STATE.DELEGATE_NAY.toString()]) - - await vote( voteId, !supports, false, holder) - - await verifyVoteYN(voteId, LDO1, 0) - - const voterState = await voting.getVoterStateMultipleAtVote(voteId, [holder]) - assertArraysEqualAsSets(voterState, [VOTER_STATE.YEA.toString()]) - }) - - // A delegated voter can overwrite a delegate's vote. - it('delegated voter can overwrite a delegates vote (voteForMulti)', async () => { - const [ delegate, holder] = [ delegate2, holder29 ] - - await assignDelegate(delegate, holder) - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - const delegatedVoters = await voting.getDelegatedVoters(delegate2, 0, 3) - assertArraysEqualAsSets(delegatedVoters, [holder29]) - - const supports = true - await attemptVoteForMultiple(voteId, supports, [holder], delegate2) - const delegatedVoterState = await voting.getVoterStateMultipleAtVote(voteId, [holder]) - assertArraysEqualAsSets(delegatedVoterState, [VOTER_STATE.DELEGATE_YEA.toString()]) - - await vote(voteId, !supports, false, holder) - - await verifyVoteYN(voteId, 0 , LDO29) - - const voterState = await voting.getVoterStateMultipleAtVote(voteId, [holder]) - assertArraysEqualAsSets(voterState, [VOTER_STATE.NAY.toString()]) - }) - - // A delegate can vote for a voter that delegated them their voting power during the active - // phase of the vote. - it('delegate can vote for a voter that delegated them their voting power during the active phase (voteFor)', async () => { - const [ delegate, holder] = [ delegate2, holder1 ] - - await assignDelegate(delegate, holder) - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - const { phase } = await voting.getVote(voteId) - assert.equal(phase, MAIN_PHASE) - - await assignDelegate(delegate1, holder) - const delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 3) - assertArraysEqualAsSets(delegatedVoters, [ holder1 ]) - - await attemptVoteFor(voteId, false, holder, delegate1) - - await verifyVoteYN(voteId, 0, LDO1) - - const voterState = await voting.getVoterStateMultipleAtVote(voteId, [holder]) - assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) - }) - - // A delegate can vote for a voter that delegated them their voting power during the active - // phase of the vote. - it('delegate can vote for a voter that delegated them their voting power during the active phase (voteForMulti)', async () => { - const [ delegate, holder] = [ delegate1, holder29 ] - - await assignDelegate(delegate, holder) - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - const { phase } = await voting.getVote(voteId) - assert.equal(phase, MAIN_PHASE) - - await assignDelegate(delegate2, holder) - const delegatedVoters = await voting.getDelegatedVoters(delegate2, 0, 3) - assertArraysEqualAsSets(delegatedVoters, [holder29]) - - await attemptVoteForMultiple(voteId, true, [holder], delegate2) - - await verifyVoteYN(voteId, LDO29, 0) - - const voterState = await voting.getVoterStateMultipleAtVote(voteId, [holder]) - assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) - }) - - // A delegate can't vote for a voter that acquired voting power during the active phase of the vote. - it('delegate cant vote for a voter that acquired voting power during the active phase of the vote (voteFor)', async () => { - const [ delegate, holder] = [ delegate1, holder1 ] - - await assignDelegate(delegate, holder) - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - const { phase } = await voting.getVote(voteId) - assert.equal(phase, MAIN_PHASE) - - await assignDelegate(delegate2, holder) - const delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 3) - assertArraysEqualAsSets(delegatedVoters, [ ]) - - await assertRevert(voting.attemptVoteFor(voteId, false, holder, {from: delegate1}), ERRORS.VOTING_CAN_NOT_VOTE_FOR) - }) - - // A delegate can't vote for a voter that acquired voting power during the active phase of the vote. - it('delegate cant vote for a voter that acquired voting power during the active phase of the vote (voteForMulti)', async () => { - const [ delegate, holder] = [ delegate2, holder29 ] - - await assignDelegate(delegate, holder) - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - const { phase } = await voting.getVote(voteId) - assert.equal(phase, MAIN_PHASE) - - await assignDelegate(delegate1, holder) - const delegatedVoters = await voting.getDelegatedVoters(delegate2, 0, 3) - assertArraysEqualAsSets(delegatedVoters, []) - - await assertRevert(voting.attemptVoteForMultiple(voteId, true, [holder], {from: delegate2}), ERRORS.VOTING_CAN_NOT_VOTE_FOR) - - }) - - // If a delegated voter lost or gain some voting power after the start of the vote, a delegate - // would still apply the full voting power of the delegated voter (at the vote's snapshot) - it('delegate vote by snapshot vp not current (voteFor)', async () => { - const delegateList= [ [delegate1, holder1], [delegate1, holder20] ] - - for (const [delegate, holder] of delegateList) { - await assignDelegate(delegate, holder) - } - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - await token.generateTokens(holder1, bigExp(2, decimals)) - await token.destroyTokens(holder20, bigExp(5, decimals)) - - const delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 3) - assertArraysEqualAsSets(delegatedVoters, [holder1,holder20]) - - for (const holder of delegatedVoters) { - await attemptVoteFor(voteId, false, holder, delegate1) - } - await verifyVoteYN(voteId, 0, LDO1.add(LDO20)) - - const voterState = await voting.getVoterStateMultipleAtVote(voteId, delegatedVoters) - assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) - }) - - // If a delegated voter lost or gain some voting power after the start of the vote, a delegate - // would still apply the full voting power of the delegated voter (at the vote's snapshot) - it('delegate vote by snapshot vp not current (voteForMulti)', async () => { - const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] - for (const [delegate, holder] of delegateList) { - await assignDelegate(delegate, holder) - } - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - await token.generateTokens(holder29, bigExp(2, decimals)) - await token.destroyTokens(holder51, bigExp(5, decimals)) - - const delegatedVoters = await voting.getDelegatedVoters(delegate2, 0, 3) - assertArraysEqualAsSets(delegatedVoters, [holder29,holder51]) - - await attemptVoteForMultiple(voteId, true, delegatedVoters, delegate2) - await verifyVoteYN(voteId, LDO51.add(LDO29), 0) - - const voterState = await voting.getVoterStateMultipleAtVote(voteId, delegatedVoters) - assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) - }) - - // A delegate can vote for all their delegated voters even if delegate voted for them before - // for different option (change mind) - it('delegate change mind (voteFor)', async () => { - const delegateList = [[delegate1, holder1], [delegate1, holder20]] - - for (const [delegate, holder] of delegateList) { - await assignDelegate(delegate, holder) - } - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - const delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 3) - - assertArraysEqualAsSets(delegatedVoters, [holder1, holder20]) - - for (const holder of delegatedVoters) { - await attemptVoteFor(voteId, false, holder, delegate1) - } - await verifyVoteYN(voteId,0, LDO1.add(LDO20)) - - for (const holder of delegatedVoters) { - await attemptVoteFor(voteId, true, holder, delegate1) - } - await verifyVoteYN(voteId, LDO1.add(LDO20), 0) - - const voterState = await voting.getVoterStateMultipleAtVote(voteId, delegatedVoters) - assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) - }) - - // A delegate can vote for all their delegated voters even if delegate voted for them before - // for different option (change mind) - it('delegate change mind (voteForMulti)', async () => { - const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] - for (const [delegate, holder] of delegateList) { - await assignDelegate(delegate, holder) - } - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - const delegatedVoters = await voting.getDelegatedVoters(delegate2, 0, 3) - assertArraysEqualAsSets(delegatedVoters, [holder29,holder51]) - - - await attemptVoteForMultiple(voteId, true, delegatedVoters, delegate2) - await verifyVoteYN(voteId, LDO51.add(LDO29), 0) - - await attemptVoteForMultiple(voteId, false, delegatedVoters, delegate2) - await verifyVoteYN(voteId, 0, LDO51.add(LDO29)) - - const voterState = await voting.getVoterStateMultipleAtVote(voteId, delegatedVoters) - assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) - }) - - // A delegate can vote for all their delegated voters even if delegate voted for them before - // for "no" option during the objection phase.(change mind) - it('delegate vote "yes" in main phase, delegate vote "no" in objection (voteFor)', async () => { - const delegateList = [[delegate1, holder1], [delegate1, holder20]] - - for (const [delegate, holder] of delegateList) { - await assignDelegate(delegate, holder) - } - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - const delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 3) - assertArraysEqualAsSets(delegatedVoters, [holder1, holder20]) - - for (const holder of delegatedVoters) { - await attemptVoteFor(voteId, true, holder, delegate1) - } - - await voting.mockIncreaseTime(votingDuration - objectionPhase) - await verifyVoteYN(voteId, LDO1.add(LDO20), 0) - - const { phase } = await voting.getVote(voteId) - assert.equal(phase, OBJECTION_PHASE) - - for (const holder of delegatedVoters) { - await attemptVoteFor(voteId, false, holder, delegate1) - } - await verifyVoteYN(voteId, 0, LDO1.add(LDO20)) - - const voterState = await voting.getVoterStateMultipleAtVote(voteId, delegatedVoters) - assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) - }) - - // A delegate can vote for all their delegated voters even if delegate voted for them before - // for "no" option during the objection phase.(change mind) - it('delegate vote "yes" in main phase, delegate vote "no" in objection (voteForMulti)', async () => { - const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] - for (const [delegate, holder] of delegateList) { - await assignDelegate(delegate, holder) - } - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - const delegatedVoters = await voting.getDelegatedVoters(delegate2, 0, 3) - assertArraysEqualAsSets(delegatedVoters, [holder29,holder51]) - - await attemptVoteForMultiple(voteId, true, delegatedVoters, delegate2) - - await voting.mockIncreaseTime(votingDuration - objectionPhase) - await verifyVoteYN(voteId, LDO51.add(LDO29), 0) - - const { phase } = await voting.getVote(voteId) - assert.equal(phase, OBJECTION_PHASE) - - await attemptVoteForMultiple(voteId, false, delegatedVoters, delegate2) - - await verifyVoteYN(voteId, 0, LDO51.add(LDO29)) - - const voterState = await voting.getVoterStateMultipleAtVote(voteId, delegatedVoters) - assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) - }) - - // A new delegate can vote "no" in objection phase for all their delegated voters - // even if old delegate voted for "yes" them before - it('delegate vote "yes" in main phase, new delegate vote "no" in objection (voteFor)', async () => { - const delegateList = [[delegate1, holder1], [delegate1, holder20]] - - for (const [delegate, holder] of delegateList) { - await assignDelegate(delegate, holder) - } - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - const delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 3) - assertArraysEqualAsSets(delegatedVoters, [holder1, holder20]) - - for (const holder of delegatedVoters) { - await attemptVoteFor(voteId, true, holder, delegate1) - } - - await voting.mockIncreaseTime(votingDuration - objectionPhase) - await verifyVoteYN(voteId, LDO1.add(LDO20), 0) - - const { phase } = await voting.getVote(voteId) - assert.equal(phase, OBJECTION_PHASE) - - const objectorList = [[delegate2, holder1], [delegate2, holder20]] - - for (const [delegate, holder] of objectorList) { - await assignDelegate(delegate, holder) - } - - for (const [ delegate , holder] of objectorList) { - await attemptVoteFor(voteId, false, holder, delegate) - } - await verifyVoteYN(voteId, 0, LDO1.add(LDO20)) - - const voterState = await voting.getVoterStateMultipleAtVote(voteId, delegatedVoters) - assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) - }) - - // A new delegate can vote "no" in objection phase for all their delegated voters - // even if old delegate voted for "yes" them before - it('delegate vote "yes" in main phase, new delegate vote "no" in objection (voteForMulti)', async () => { - const delegateList= [[delegate2, holder29], [delegate2, holder51]] - for (const [delegate, holder] of delegateList) { - await assignDelegate(delegate, holder) - } - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - const delegatedVoters = await voting.getDelegatedVoters(delegate2, 0, 3) - assertArraysEqualAsSets(delegatedVoters, [holder29, holder51]) - - await attemptVoteForMultiple(voteId, true, delegatedVoters, delegate2) - - await voting.mockIncreaseTime(votingDuration - objectionPhase) - await verifyVoteYN(voteId, LDO51.add(LDO29), 0) - - const { phase } = await voting.getVote(voteId) - assert.equal(phase, OBJECTION_PHASE) - - const objectorList = [[delegate1, holder29], [delegate1, holder51]] - - for (const [delegate, holder] of objectorList) { - await assignDelegate(delegate, holder) - } - - await attemptVoteForMultiple(voteId, false, delegatedVoters, delegate1) - - await verifyVoteYN(voteId, 0, LDO51.add(LDO29)) - - const voterState = await voting.getVoterStateMultipleAtVote(voteId, delegatedVoters) - assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_NAY.toString()]) - }) - - // A delegate is voting "yea" at the last moment of a vote's main phase. Delegated voters - // should be able to overpower the delegate during the objection phase. - it('delegate vote in main phase, voter overpower in objection (voteFor)', async () => { - const delegateList = [[delegate1, holder1], [delegate1, holder20]] - - for (const [delegate, holder] of delegateList) { - await assignDelegate(delegate, holder) - } - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - const delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 3) - assertArraysEqualAsSets(delegatedVoters, [holder1, holder20]) - - for (const holder of delegatedVoters) { - await attemptVoteFor(voteId, true, holder, delegate1) - } - - await voting.mockIncreaseTime(votingDuration - objectionPhase) - await verifyVoteYN(voteId, LDO1.add(LDO20), 0) - - const { phase } = await voting.getVote(voteId) - assert.equal(phase, OBJECTION_PHASE) - - for (const holder of delegatedVoters) { - await vote(voteId, false, false, holder) - } - await verifyVoteYN(voteId, 0, LDO1.add(LDO20)) - - const voterState = await voting.getVoterStateMultipleAtVote(voteId, delegatedVoters) - assertArraysEqualAsSets(voterState, [VOTER_STATE.NAY.toString()]) - }) - - // A delegate is voting "yea" at the last moment of a vote's main phase. Delegated voters - // should be able to overpower the delegate during the objection phase. - it('delegate vote in main phase, voter overpower in objection (voteForMulti)', async () => { - const delegateList= [ [delegate2, holder29], [delegate2, holder51] ] - for (const [delegate, holder] of delegateList) { - await assignDelegate(delegate, holder) - } - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - const delegatedVoters = await voting.getDelegatedVoters(delegate2, 0, 3) - assertArraysEqualAsSets(delegatedVoters, [holder29,holder51]) - - await attemptVoteForMultiple(voteId, true, delegatedVoters, delegate2) - - await voting.mockIncreaseTime(votingDuration - objectionPhase) - await verifyVoteYN(voteId, LDO51.add(LDO29), 0) - - const { phase } = await voting.getVote(voteId) - assert.equal(phase, OBJECTION_PHASE) - - for (const holder of delegatedVoters) { - await vote(voteId, false, false, holder) - } - await verifyVoteYN(voteId, 0, LDO51.add(LDO29)) - - const voterState = await voting.getVoterStateMultipleAtVote(voteId, delegatedVoters) - assertArraysEqualAsSets(voterState, [VOTER_STATE.NAY.toString()]) - }) - - // If a delegate was spammed by a large amount of fake delegated voters, they can still easily - // retrieve an actual voters list and vote for that list. - it('delegate can vote after spam', async () => { - await assignDelegate(delegate2, holder29) - for (const holder of spamHolders) { - await token.generateTokens(holder, LDO3) - await assignDelegate(delegate2, holder) - } - await assignDelegate(delegate2, holder51) - - const voteId = createdVoteId(await voting.newVote(EMPTY_CALLS_SCRIPT, 'metadata')) - - const delegatedVoters = await voting.getDelegatedVoters(delegate2, 0, 600) - assertArraysEqualAsSets(delegatedVoters, [holder29, ...spamHolders, holder51]) - - await attemptVoteForMultiple(voteId, true, [holder29, holder51], delegate2) - - await verifyVoteYN(voteId, LDO51.add(LDO29), 0) - - const voterState = await voting.getVoterStateMultipleAtVote(voteId, [holder29, holder51]) - assertArraysEqualAsSets(voterState, [VOTER_STATE.DELEGATE_YEA.toString()]) - - const voterStateSpam = await voting.getVoterStateMultipleAtVote(voteId, spamHolders) - assertArraysEqualAsSets(voterStateSpam, [VOTER_STATE.ABSENT.toString()]) - }).timeout(60_000); - - ///end }) - context('delegated voters getters', () => { - const neededSupport = pct16(50) - const minimumAcceptanceQuorum = pct16(20) - const decimals = 18 - const defaultLimit = 100 - const voters = [{ - account: holder1, - balance: bigExp(1, decimals) - },{ - account: holder2, - balance: bigExp(2, decimals) - }, { - account: holder20, - balance: bigExp(20, decimals) - }, { - account: holder29, - balance: bigExp(29, decimals) - }] - let voteId - + context('delegation state getters', () => { beforeEach(async () => { - token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', decimals, 'n', true) // empty parameters minime - - await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, 0) - - for (let i = 0; i < voters.length; i++) { - await token.generateTokens(voters[i].account, voters[i].balance) - await voting.assignDelegate(delegate1, {from: voters[i].account}) + // Generate voting tokens for voters and assign delegate1 as a delegate for all of them + for (const voter of voters) { + await token.generateTokens(voter, votersBalances[voter]) + await voting.assignDelegate(delegate1, { from: voter }) } + }) - executionTarget = await ExecutionTarget.new() - - const action = {to: executionTarget.address, calldata: executionTarget.contract.methods.execute().encodeABI()} - script = encodeCallScript([action, action]) - - const receipt = await voting.methods['newVote(bytes,string)'](script, 'metadata', {from: holder51}); - voteId = getEventArgument(receipt, 'StartVote', 'voteId') + // + // getDelegatedVotersCount + // + it('should return empty array if there are no delegated voters', async () => { + const delegatedVotersCount = (await voting.getDelegatedVotersCount(delegate2)).toNumber() + assert(delegatedVotersCount === 0) }) it('should return correct delegated voters count', async () => { @@ -887,12 +240,17 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 assert(delegatedVotersCount === voters.length) }) - it(`revert if "_delegate" is zero address`, async () => { + it(`getDelegatedVotersCount: revert if "_delegate" is zero address`, async () => { await assertRevert( voting.getDelegatedVotersCount(ZERO_ADDRESS), ERRORS.VOTING_ZERO_ADDRESS_PASSED ) + }) + // + // getDelegatedVoters + // + it(`getDelegatedVoters: revert if "_delegate" is zero address`, async () => { await assertRevert( voting.getDelegatedVoters(ZERO_ADDRESS, 0, defaultLimit), ERRORS.VOTING_ZERO_ADDRESS_PASSED @@ -905,18 +263,16 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 }) it(`if offset is more than length, return empty array`, async () => { - const delegatedVoters = await voting.getDelegatedVoters(nonHolder, 100, defaultLimit) + const delegatedVoters = await voting.getDelegatedVoters(nonHolder, voters.length + 1, defaultLimit) assert(delegatedVoters.length === 0, 'votersList should be empty') }) - it(`if delegatedVoters array length is 0, return two empty arrays`, async () => { + it(`if delegatedVoters array length is 0, return empty array`, async () => { const delegatedVoters = await voting.getDelegatedVoters(nonHolder, 0, defaultLimit) - const delegatedVotersVotingPower = await voting.getVotingPowerMultiple(delegatedVoters) assert(delegatedVoters.length === 0, 'votersList should be empty') - assert(delegatedVotersVotingPower.length === 0, 'votingPowerList should be empty') }) - it(`should return correct delegated voters data if offset + limit >= votersCount`, async () => { + it(`should return correct delegatedVoters array if offset + limit >= votersCount`, async () => { const offset = 2 const limit = 5 const delegatedVoters = await voting.getDelegatedVoters(delegate1, offset, limit) @@ -926,13 +282,7 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 assert(delegatedVoters.length === delegatedVotersCountToReturn) const votersSlice = voters.slice(offset, delegatedVotersCount) - const votersListSlice = votersSlice.map(voter => voter.account) - assertArraysEqualAsSets(delegatedVoters, votersListSlice, 'votersList should be correct') - - const votingPowerListSlice = votersSlice.map((voter) => voter.balance.toString()) - - const votingPowerList = (await voting.getVotingPowerMultiple(delegatedVoters)).map(votingPower => votingPower.toString()) - assertArraysEqualAsSets(votingPowerList, votingPowerListSlice, 'votingPowerList should be correct') + assertArraysEqualAsSets(delegatedVoters, votersSlice, 'votersList should be correct') }) it(`should return correct delegated voters data if offset + limit < votersCount`, async () => { @@ -943,14 +293,12 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 assert(delegatedVoters.length === limit) const votersSlice = voters.slice(offset, offset + limit) - const votersListSlice = votersSlice.map(voter => voter.account) - assertArraysEqualAsSets(delegatedVoters, votersListSlice, 'votersList should be correct') - - const votingPowerListSlice = votersSlice.map((voter) => voter.balance.toString()) - const votingPowerList = (await voting.getVotingPowerMultiple(delegatedVoters)).map(votingPower => votingPower.toString()) - assertArraysEqualAsSets(votingPowerList, votingPowerListSlice, 'votingPowerList should be correct') + assertArraysEqualAsSets(delegatedVoters, votersSlice, 'votersList should be correct') }) + // + // getDelegate + // it(`revert if _voter is zero address`, async () => { await assertRevert( voting.getDelegate(ZERO_ADDRESS), @@ -964,144 +312,135 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 }) it(`can get voter's delegate address`, async () => { - const delegate = await voting.getDelegate(holder1) + const delegate = await voting.getDelegate(voters[0]) assert.equal(delegate, delegate1, 'should return delegate1 address') }) - it(`voting power getters`, async () => { - const initialVotingPower = voters.map(v => v.balance.toString()) - const votersAddresses = voters.map(v => v.account) + // + // getVotingPowerMultiple, getVotingPowerMultipleAtVote + // + it(`can't get voting power at vote that doesn't exist`, async () => { + voteId = await startEmptyVote() + + await assertRevert( + voting.getVotingPowerMultipleAtVote(voteId + 1, voters), + ERRORS.VOTING_NO_VOTE + ) + }) - await assertRevert(voting.getVotingPowerMultipleAtVote(voteId + 1, votersAddresses), ERRORS.VOTING_NO_VOTE) + it(`voting power getters are working correctly`, async () => { + voteId = await startEmptyVote() - const currentVotingPower = await voting.getVotingPowerMultiple(votersAddresses) + const initialVotingPower = Object.values(votersBalances).map(balance => balance.toString()) + const currentVotingPower = await voting.getVotingPowerMultiple(voters) assertArraysEqualAsSets(currentVotingPower, initialVotingPower, 'current voting power values should match') const updatedVoterIndex = 0 - const vpAddition = bigExp(1, decimals) - await token.generateTokens(voters[updatedVoterIndex].account, vpAddition) + const vpAddition = bigExp(1, TOKEN_DECIMALS) + await token.generateTokens(voters[updatedVoterIndex], vpAddition) const updatedVotingPowerToCompare = voters.map((v, i) => { if (i === updatedVoterIndex) { - return v.balance.add(vpAddition).toString() + return votersBalances[v].add(vpAddition).toString() } - return v.balance.toString() + return votersBalances[v].toString() }) - const updatedVotingPower = await voting.getVotingPowerMultiple(votersAddresses) + const updatedVotingPower = await voting.getVotingPowerMultiple(voters) assertArraysEqualAsSets(updatedVotingPower, updatedVotingPowerToCompare, 'current voting power values should match after update') - const votingPowerAtVote = await voting.getVotingPowerMultipleAtVote(voteId, votersAddresses) + const votingPowerAtVote = await voting.getVotingPowerMultipleAtVote(voteId, voters) assertArraysEqualAsSets(votingPowerAtVote, initialVotingPower, 'voting power at vote should match vp without update') }) - }) - - context('voting as delegate', () => { - let script, voteId, creator, metadata - - const neededSupport = pct16(50) - const minimumAcceptanceQuorum = pct16(20) - const decimals = 18 - const voters = [{ - account: holder1, - balance: bigExp(1, decimals) - }, { - account: holder2, - balance: bigExp(2, decimals) - }, { - account: holder20, - balance: bigExp(20, decimals) - }, { - account: holder29, - balance: bigExp(29, decimals) - }, { - account: holder51, - balance: bigExp(51, decimals) - }] - - beforeEach(async () => { - token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', decimals, 'n', true) // empty parameters minime - await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, objectionPhase) + // + // getVoterStateMultipleAtVote + // + it(`getVoterStateMultipleAtVote: revert if vote does not exist`, async () => { + voteId = await startEmptyVote() - for (let i = 0; i < voters.length; i++) { - await token.generateTokens(voters[i].account, voters[i].balance) - await voting.assignDelegate(delegate1, {from: voters[i].account}) - } - await token.generateTokens(ZERO_ADDRESS, bigExp(1, decimals)) + await assertRevert( + voting.getVoterStateMultipleAtVote(voteId + 1, [voters[0]]), + ERRORS.VOTING_NO_VOTE + ) + }) - executionTarget = await ExecutionTarget.new() + it(`can get correct voterState for a list of voters`, async () => { + voteId = await startEmptyVote() - const action = {to: executionTarget.address, calldata: executionTarget.contract.methods.execute().encodeABI()} - script = encodeCallScript([action, action]) + const votersSlice = voters.slice(0, 3) - await voting.unassignDelegate({from: holder1}) - await token.transfer(holder51, bigExp(2, decimals), { from: holder2 }) + await voting.vote(voteId, VOTE_YEA_VALUE, false, { from: votersSlice[0] }) + await voting.vote(voteId, VOTE_NAY_VALUE, false, { from: votersSlice[1] }) + await voting.attemptVoteForMultiple(voteId, false, [votersSlice[2]], { from: delegate1 }) - const receipt = await voting.methods['newVote(bytes,string)'](script, 'metadata', {from: holder51}); - voteId = getEventArgument(receipt, 'StartVote', 'voteId') - creator = getEventArgument(receipt, 'StartVote', 'creator') - metadata = getEventArgument(receipt, 'StartVote', 'metadata') + const votersState = await voting.getVoterStateMultipleAtVote(voteId, votersSlice) + assert.equal(votersState[0], VOTER_STATE.YEA) + assert.equal(votersState[1], VOTER_STATE.NAY) + assert.equal(votersState[2], VOTER_STATE.DELEGATE_NAY) }) + }) - it(`getVoterStateMultipleAtVote`, async () => { - await voting.vote(voteId, true, false, { from: holder20 }) - await voting.vote(voteId, false, false, { from: holder29 }) - await voting.attemptVoteForMultiple(voteId, false, [holder51], {from: delegate1}) + context('voting as delegate', () => { + beforeEach(async () => { + for (const voter of voters) { + await token.generateTokens(voter, votersBalances[voter]) + await voting.assignDelegate(delegate1, { from: voter }) + } - await assertRevert(voting.getVoterStateMultipleAtVote(voteId + 1, [holder51]), ERRORS.VOTING_NO_VOTE) - const votersState = await voting.getVoterStateMultipleAtVote(voteId, [holder20, holder29, holder51]) - assert.equal(votersState[0], VOTER_STATE.YEA, `holder20 should have 'yea' state`) - assert.equal(votersState[1], VOTER_STATE.NAY, `holder29 should have 'nay' state`) - assert.equal(votersState[2], VOTER_STATE.DELEGATE_NAY, `holder51 should have 'delegateNay' state`) + voteId = await startEmptyVote() }) it(`revert if vote does not exist`, async () => { await assertRevert( - voting.attemptVoteForMultiple(voteId + 1, false, [holder51], {from: delegate1}), + voting.attemptVoteForMultiple(voteId + 1, VOTE_NAY_VALUE, voters, { from: delegate1 }), ERRORS.VOTING_NO_VOTE ) }) it(`revert if vote has already ended`, async () => { - await voting.mockIncreaseTime(votingDuration + 1) + await voting.mockIncreaseTime(VOTING_DURATION + 1) await assertRevert( - voting.attemptVoteForMultiple(voteId, false, [holder51], {from: delegate1}), + voting.attemptVoteForMultiple(voteId, VOTE_NAY_VALUE, voters, { from: delegate1 }), ERRORS.VOTING_CAN_NOT_VOTE ) }) it(`revert if vote has already been executed`, async () => { - await voting.vote(voteId, true, true, { from: holder51 }) - await voting.mockIncreaseTime(votingDuration + 1) + const voter = voters[voters.length - 1] + await voting.vote(voteId, VOTE_YEA_VALUE, false, { from: voter }) + await voting.mockIncreaseTime(VOTING_DURATION + 1) await voting.executeVote(voteId) await assertRevert( - voting.attemptVoteForMultiple(voteId, true, [holder51], {from: delegate1}), + voting.attemptVoteForMultiple(voteId, VOTE_YEA_VALUE, [voter], { from: delegate1 }), ERRORS.VOTING_CAN_NOT_VOTE ) }) it(`revert if trying to vote 'yea' during objection phase`, async () => { - await voting.mockIncreaseTime(mainPhase + 200) + await voting.mockIncreaseTime(VOTING_DURATION - OBJECTION_PHASE_DURATION) + assert.equal((await voting.getVote(voteId)).phase, VOTE_PHASE.OBJECTION, 'vote should be in the objection phase') await assertRevert( - voting.attemptVoteForMultiple(voteId, true, [holder51], {from: delegate1}), + voting.attemptVoteForMultiple(voteId, VOTE_YEA_VALUE, voters, { from: delegate1 }), ERRORS.VOTING_CAN_NOT_VOTE ) }) - it(`revert if one of the voters has 0 LDO`, async () => { - // holder 2 has 0 LDO + it(`revert if one of the voters has 0 voting power`, async () => { + await voting.assignDelegate(delegate1, { from: nonHolder }) await assertRevert( - voting.attemptVoteForMultiple(voteId, true, [holder51, holder2, holder1], {from: delegate1}), + voting.attemptVoteForMultiple(voteId, VOTE_YEA_VALUE, [nonHolder, ...voters], { from: delegate1 }), ERRORS.VOTING_NO_VOTING_POWER ) - }) + }) it(`skip zero address passed`, async () => { + await token.generateTokens(ZERO_ADDRESS, bigExp(1, TOKEN_DECIMALS)) + voteId = await startEmptyVote() + const voter = voters[0] // Skip if zero address is one of the voters - let tx = await voting.attemptVoteForMultiple(voteId, true, [holder51, ZERO_ADDRESS], {from: delegate1}) - - assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) - assertEvent(tx, 'CastVote', {expectedArgs: {voteId, voter: holder51, supports: true}}) + let tx = await voting.attemptVoteForMultiple(voteId, VOTE_YEA_VALUE, [voter, ZERO_ADDRESS], { from: delegate1 }) + assertAmountOfEvents(tx, 'CastVote', { expectedAmount: 1 }) + assertEvent(tx, 'CastVote', { expectedArgs: { voteId, voter, supports: VOTE_YEA_VALUE } }) // Revert if zero address is a delegate (can't delegate to zero address) // This test was added to improve test coverage @@ -1114,178 +453,461 @@ contract('Voting App (delegation)', ([root, holder1, holder2, holder20, holder29 }); // The revert is expected because the delegate is zero address, so it's - // impossible to delegate to it. But holder51 will be skipped. + // impossible to delegate to it. But voter will be skipped. await assertRevert( - voting.attemptVoteForMultiple(voteId, true, [holder51], {from: signerZero.address}), + voting.attemptVoteForMultiple(voteId, VOTE_YEA_VALUE, [voter], { from: signerZero.address }), ERRORS.VOTING_CAN_NOT_VOTE_FOR ) }) - it(`one of the voters voted beforehand`, async () => { - await voting.vote(voteId, false, false, { from: holder29 }) - const tx = await voting.attemptVoteForMultiple(voteId, true, [holder20, holder29], {from: delegate1}); + it(`one of the voters has voted beforehand`, async () => { + const [selfVoter, ...restVoters] = voters + await voting.vote(voteId, VOTE_NAY_VALUE, false, { from: selfVoter }) + const tx = await voting.attemptVoteForMultiple(voteId, VOTE_YEA_VALUE, voters, { from: delegate1 }) - assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) - assertAmountOfEvents(tx, 'AttemptCastVoteAsDelegate', {expectedAmount: 1}) - assertEvent(tx, 'CastVote', {expectedArgs: {voteId, voter: holder20, supports: true}}) - assertEvent(tx, 'AttemptCastVoteAsDelegate', {expectedArgs: {voteId, delegate: delegate1}}) + assertAmountOfEvents(tx, 'CastVote', { expectedAmount: restVoters.length }) + assertAmountOfEvents(tx, 'AttemptCastVoteAsDelegate', { expectedAmount: 1 }) + for (let index = 0; index < restVoters.length; index++) { + assertEvent(tx, 'CastVote', { index, expectedArgs: { voteId, voter: restVoters[index], supports: VOTE_YEA_VALUE } }) + } + assertEvent(tx, 'AttemptCastVoteAsDelegate', { expectedArgs: { voteId, delegate: delegate1 } }) const votersFromEvent = getEventArgument(tx, 'AttemptCastVoteAsDelegate', 'voters') - assertArraysEqualAsSets([holder20, holder29], votersFromEvent) + assertArraysEqualAsSets(votersFromEvent, voters) - assert.equal(await voting.getVoterState(voteId, holder29), VOTER_STATE.NAY, `holder29 should have 'nay' state`) - assert.equal(await voting.getVoterState(voteId, holder20), VOTER_STATE.DELEGATE_YEA, `holder20 should have 'delegateYea' state`) + assert.equal(await voting.getVoterState(voteId, selfVoter), VOTER_STATE.NAY, `selfVoter should have 'nay' state`) + const votersState = await voting.getVoterStateMultipleAtVote(voteId, restVoters) + votersState.every((state) => { + assert.equal(state, VOTER_STATE.DELEGATE_YEA, `voter should have 'delegateYea' state`) + }) }) it(`vote for multiple with duplicates`, async () => { - const tx = await voting.attemptVoteForMultiple(voteId, false, [holder29, holder29], {from: delegate1}); + const duplicatedVoter = voters[0] + const votersListWithDuplicates = [duplicatedVoter, ...voters] + const tx = await voting.attemptVoteForMultiple(voteId, VOTE_NAY_VALUE, votersListWithDuplicates, { from: delegate1 }) - assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 2}) - assertAmountOfEvents(tx, 'AttemptCastVoteAsDelegate', {expectedAmount: 1}) - assert.equal(await voting.getVoterState(voteId, holder29), VOTER_STATE.DELEGATE_NAY, `holder29 should have 'delegateNay' state`) - const vote = await voting.getVote(voteId) - assertBn(vote.nay, bigExp(29, decimals), 'nay should be 29') + // The amount of CastEvents includes the duplicated voter + assertAmountOfEvents(tx, 'CastVote', { expectedAmount: votersListWithDuplicates.length }) + assertAmountOfEvents(tx, 'AttemptCastVoteAsDelegate', { expectedAmount: 1 }) + for (let index = 0; index < votersListWithDuplicates.length; index++) { + assertEvent(tx, 'CastVote', { index, expectedArgs: { voteId, voter: votersListWithDuplicates[index], supports: VOTE_NAY_VALUE } }) + } + assertEvent(tx, 'AttemptCastVoteAsDelegate', { expectedArgs: { voteId, delegate: delegate1 } }) + const votersFromEvent = getEventArgument(tx, 'AttemptCastVoteAsDelegate', 'voters') + assertArraysEqualAsSets(votersFromEvent, votersListWithDuplicates) + assert.equal(await voting.getVoterState(voteId, duplicatedVoter), VOTER_STATE.DELEGATE_NAY, `duplicatedVoter should have 'delegateNay' state`) + + const voteNayVP = (await voting.getVote(voteId)).nay + const votersVP = await voting.getVotingPowerMultipleAtVote(voteId, voters) + const votersVPSum = getVotingPowerSum(votersVP) + assertBn(voteNayVP, votersVPSum, 'nay should be the sum of all VP with duplicates') }) it(`vote for empty list`, async () => { await assertRevert( - voting.attemptVoteForMultiple(voteId, false, [], {from: delegate1}), + voting.attemptVoteForMultiple(voteId, false, [], { from: delegate1 }), ERRORS.VOTING_CAN_NOT_VOTE_FOR ) }) it(`skipped vote for multiple for all voters from list`, async () => { - await voting.vote(voteId, false, false, { from: holder20 }) - await voting.vote(voteId, false, false, { from: holder29 }) + for (const voter of voters) { + await voting.vote(voteId, VOTE_NAY_VALUE, false, { from: voter }) + } await assertRevert( - voting.attemptVoteForMultiple(voteId, false, [holder20, holder29], {from: delegate1}), + voting.attemptVoteForMultiple(voteId, VOTE_NAY_VALUE, voters, { from: delegate1 }), ERRORS.VOTING_CAN_NOT_VOTE_FOR ) }) it(`successful vote for multiple`, async () => { - const delegatedVotersAddresses = await voting.getDelegatedVoters(delegate1, 0, 100) + const delegatedVotersAddresses = await voting.getDelegatedVoters(delegate1, 0, defaultLimit) const delegatedVotersVotingPower = await voting.getVotingPowerMultipleAtVote(voteId, delegatedVotersAddresses) - const filteredDelegatedVoters = [] + const delegatedVotersState = (await voting.getVoterStateMultipleAtVote(voteId, delegatedVotersAddresses)).map(state => state.toString()) + const eligibleDelegatedVoters = [] for (let i = 0; i < delegatedVotersAddresses.length; i++) { const votingPower = delegatedVotersVotingPower[i] - if (votingPower.gt(bigExp(0, decimals))) { - filteredDelegatedVoters.push({address: delegatedVotersAddresses[i], votingPower}) + const voterState = delegatedVotersState[i] + if (votingPower.gt(ZERO_BN) && voterState !== VOTER_STATE.YEA && voterState !== VOTER_STATE.NAY) { + eligibleDelegatedVoters.push({ address: delegatedVotersAddresses[i], votingPower }) } } - const filteredDelegatedVotersAddresses = filteredDelegatedVoters.map(({address}) => address) + const eligibleDelegatedVotersAddresses = eligibleDelegatedVoters.map(({ address }) => address) const tx = await voting.attemptVoteForMultiple( voteId, - false, - filteredDelegatedVotersAddresses, - {from: delegate1} - ); + VOTE_NAY_VALUE, + eligibleDelegatedVotersAddresses, + { from: delegate1 } + ) // Check amount of events - assertAmountOfEvents(tx, 'CastVote', {expectedAmount: filteredDelegatedVoters.length}) - assertAmountOfEvents(tx, 'AttemptCastVoteAsDelegate', {expectedAmount: 1}) + assertAmountOfEvents(tx, 'CastVote', { expectedAmount: eligibleDelegatedVoters.length }) + assertAmountOfEvents(tx, 'AttemptCastVoteAsDelegate', { expectedAmount: 1 }) // Check events content - for (let i = 0; i < filteredDelegatedVoters.length; i++) { - const {address, votingPower} = filteredDelegatedVoters[i] - assertEvent(tx, 'CastVote', {index: i, expectedArgs: {voteId, voter: address, supports: false, stake: votingPower}}) + for (let index = 0; index < eligibleDelegatedVoters.length; index++) { + const { address, votingPower } = eligibleDelegatedVoters[index] + assertEvent(tx, 'CastVote', { index, expectedArgs: { voteId, voter: address, supports: VOTE_NAY_VALUE, stake: votingPower } }) } - assertEvent(tx, 'AttemptCastVoteAsDelegate', {expectedArgs: {voteId, delegate: delegate1}}) + assertEvent(tx, 'AttemptCastVoteAsDelegate', { expectedArgs: { voteId, delegate: delegate1 } }) const votersFromEvent = getEventArgument(tx, 'AttemptCastVoteAsDelegate', 'voters') - assertArraysEqualAsSets(filteredDelegatedVoters, votersFromEvent) + assertArraysEqualAsSets(eligibleDelegatedVotersAddresses, votersFromEvent) // Check voters' state - const votersState = await voting.getVoterStateMultipleAtVote(voteId, filteredDelegatedVotersAddresses) + const votersState = await voting.getVoterStateMultipleAtVote(voteId, eligibleDelegatedVotersAddresses) votersState.every((state) => { assert.equal(state, VOTER_STATE.DELEGATE_NAY.toString(), `voter should have 'delegateNay' state`) }) // Check applied VP const vote = await voting.getVote(voteId) - const votingPowerSum = filteredDelegatedVoters.reduce( - (sum, {votingPower}) => sum.add(votingPower), - bigExp(0, decimals) + const votingPowerSum = eligibleDelegatedVoters.reduce( + (sum, { votingPower }) => sum.add(votingPower), + ZERO_BN ) - - assertBn(vote.yea, bigExp(0, decimals), 'yea should be 0') assertBn(vote.nay, votingPowerSum, 'nay should be sum of all VP') }) it(`successful vote for single`, async () => { - const tx = await voting.attemptVoteFor(voteId, false, holder29, {from: delegate1}) + const voter = voters[0] + const tx = await voting.attemptVoteFor(voteId, VOTE_NAY_VALUE, voter, { from: delegate1 }) // Check amount of events - assertAmountOfEvents(tx, 'CastVote', {expectedAmount: 1}) - assertAmountOfEvents(tx, 'AttemptCastVoteAsDelegate', {expectedAmount: 1}) - - const holder29VP = bigExp(29, decimals) + assertAmountOfEvents(tx, 'CastVote', { expectedAmount: 1 }) + assertAmountOfEvents(tx, 'AttemptCastVoteAsDelegate', { expectedAmount: 1 }) // Check events content - assertEvent(tx, 'CastVote', { expectedArgs: {voteId, voter: holder29, supports: false, stake: holder29VP}}) - assertEvent(tx, 'AttemptCastVoteAsDelegate', { expectedArgs: {voteId, delegate: delegate1}}) + assertEvent(tx, 'CastVote', { expectedArgs: { voteId, voter, supports: VOTE_NAY_VALUE, stake: votersBalances[voter] } }) + assertEvent(tx, 'AttemptCastVoteAsDelegate', { expectedArgs: { voteId, delegate: delegate1 } }) const votersFromEvent = getEventArgument(tx, 'AttemptCastVoteAsDelegate', 'voters') - assertArraysEqualAsSets([holder29], votersFromEvent) + assertArraysEqualAsSets(votersFromEvent, [voter]) // Check voter's state - assert.equal(await voting.getVoterState(voteId, holder29), VOTER_STATE.DELEGATE_NAY, `holder29 should have 'delegateNay' state`) + assert.equal(await voting.getVoterState(voteId, voter), VOTER_STATE.DELEGATE_NAY, `voter should have 'delegateNay' state`) // Check applied VP const vote = await voting.getVote(voteId) - assertBn(vote.yea, bigExp(0, decimals), 'yea should be 0') - assertBn(vote.nay, holder29VP, 'nay should be holder29 VP') + assertBn(vote.yea, ZERO_BN, 'yea should be 0') + assertBn(vote.nay, votersBalances[voter], `nay should be voter's VP`) }) - }) - context.skip('Gas estimation tests (should be skipped)', () => { - let script, voteId, creator, metadata + context('various scenarios', () => { + beforeEach(async () => { + for (const voter of voters) { + await token.generateTokens(voter, votersBalances[voter]) + } - const neededSupport = pct16(50) - const minimumAcceptanceQuorum = pct16(20) - const decimals = 18 + voteId = await startEmptyVote() + }) - beforeEach(async () => { - token = await MiniMeToken.new(ZERO_ADDRESS, ZERO_ADDRESS, 0, 'n', decimals, 'n', true) // empty parameters minime + it(`a delegated voter can overwrite delegate's vote during the main phase`, async () => { + const voter = voters[0] + await voting.assignDelegate(delegate1, { from: voter }) + await voting.attemptVoteFor(voteId, VOTE_NAY_VALUE, voter, { from: delegate1 }) + + let vote = await voting.getVote(voteId) + const votedNayBeforeOverwrite = vote.nay + assert.equal(await voting.getVoterState(voteId, voter), VOTER_STATE.DELEGATE_NAY) + + await voting.vote(voteId, VOTE_YEA_VALUE, false, { from: voter }) - await voting.initialize(token.address, neededSupport, minimumAcceptanceQuorum, votingDuration, objectionPhase) - await token.generateTokens(holder51, bigExp(51, decimals)) + vote = await voting.getVote(voteId) + assertBn(vote.nay, ZERO_BN, 'Applied VP for the previous vote should be removed') + assertBn(votedNayBeforeOverwrite, vote.yea, 'Applied VP should move to the overwritten option') + assert.equal(await voting.getVoterState(voteId, voter), VOTER_STATE.YEA) + + // Check that delegate can't vote on behalf of the voter after the voter has voted + await assertRevert( + voting.attemptVoteFor(voteId, VOTE_NAY_VALUE, voter, { from: delegate1 }), + ERRORS.VOTING_CAN_NOT_VOTE_FOR + ) + }) + + it(`a delegated voter can overwrite delegate's vote during the objection phase`, async () => { + const voter = voters[0] + await voting.assignDelegate(delegate1, { from: voter }) + + // The delegate votes on behalf of the voter + await voting.attemptVoteFor(voteId, VOTE_YEA_VALUE, voter, { from: delegate1 }) + let vote = await voting.getVote(voteId) + const votedYeaBeforeOverwrite = vote.yea + assert.equal(await voting.getVoterState(voteId, voter), VOTER_STATE.DELEGATE_YEA) + + // Fast-forward the vote to the objection phase + await voting.mockIncreaseTime(VOTING_DURATION - OBJECTION_PHASE_DURATION) + vote = await voting.getVote(voteId) + assert.equal(vote.phase, VOTE_PHASE.OBJECTION) + + // The voter overwrites the delegate's vote + await voting.vote(voteId, VOTE_NAY_VALUE, false, { from: voter }) + vote = await voting.getVote(voteId) + assertBn(vote.yea, ZERO_BN, 'Applied VP for the previous vote should be removed') + assertBn(votedYeaBeforeOverwrite, vote.nay, 'Applied VP should move to the overwritten option') + assert.equal(await voting.getVoterState(voteId, voter), VOTER_STATE.NAY) + }) + + it('a delegate can vote for a voter that assigned them during the open vote', async () => { + // Check for the main phase + assert.equal((await voting.getVote(voteId)).phase, VOTE_PHASE.MAIN) + + const [voter1, voter2] = voters + + await voting.assignDelegate(delegate1, { from: voter1 }) + await voting.attemptVoteFor(voteId, VOTE_YEA_VALUE, voter1, { from: delegate1 }) + assert.equal(await voting.getVoterState(voteId, voter1), VOTER_STATE.DELEGATE_YEA) + + // Check for the objection phase + await voting.mockIncreaseTime(VOTING_DURATION - OBJECTION_PHASE_DURATION) + assert.equal((await voting.getVote(voteId)).phase, VOTE_PHASE.OBJECTION) + + await voting.assignDelegate(delegate1, { from: voter2 }) + await voting.attemptVoteFor(voteId, VOTE_NAY_VALUE, voter2, { from: delegate1 }) + assert.equal(await voting.getVoterState(voteId, voter2), VOTER_STATE.DELEGATE_NAY) + }) + + it(`a delegate can't vote for a voter that changed the delegate during the vote`, async () => { + const voter = voters[0] + await voting.assignDelegate(delegate1, { from: voter }) + await voting.attemptVoteFor(voteId, VOTE_YEA_VALUE, voter, { from: delegate1 }) + assert.equal(await voting.getVoterState(voteId, voter), VOTER_STATE.DELEGATE_YEA) + + await voting.assignDelegate(delegate2, { from: voter }) + + // delegate1 tries to change their mind after being unassigned + await assertRevert( + voting.attemptVoteFor(voteId, VOTE_NAY_VALUE, voter, { from: delegate1 }), + ERRORS.VOTING_CAN_NOT_VOTE_FOR + ) + }) + + it(`a delegate's vote applies only vote's snapshot VP, not current`, async () => { + const votersSlice = voters.slice(0, 2) + for (const voter of votersSlice) { + await voting.assignDelegate(delegate1, { from: voter }) + } + + // Manipulate voting power of the delegated voters after the vote has started + await token.generateTokens(votersSlice[0], bigExp(2, TOKEN_DECIMALS)) + await token.destroyTokens(votersSlice[1], bigExp(1, TOKEN_DECIMALS)) + + await voting.attemptVoteForMultiple(voteId, VOTE_YEA_VALUE, votersSlice, { from: delegate1 }) + + // Check if the delegate's vote applied the correct voting power + const votersVotingPower = await voting.getVotingPowerMultipleAtVote(voteId, votersSlice) + const votingPowerSum = getVotingPowerSum(votersVotingPower) + const vote = await voting.getVote(voteId) + assertBn(vote.yea, votingPowerSum, 'Vote should have the correct yea value') + }) + + it('a delegate can change their mind and re-vote for other option', async () => { + const votersSlice = voters.slice(0, 2) + for (const voter of votersSlice) { + await voting.assignDelegate(delegate1, { from: voter }) + } + + await voting.attemptVoteForMultiple(voteId, VOTE_NAY_VALUE, votersSlice, { from: delegate1 }) + // Check the state after the first vote + let votersState = await voting.getVoterStateMultipleAtVote(voteId, votersSlice) + votersState.every((state) => { + assert.equal(state, VOTER_STATE.DELEGATE_NAY, `voter should have 'delegateNay' state`) + }) + let vote = await voting.getVote(voteId) + const votersVotingPower = await voting.getVotingPowerMultipleAtVote(voteId, votersSlice) + const votersVotingPowerSum = getVotingPowerSum(votersVotingPower) + assertBn(vote.nay, votersVotingPowerSum) + + // Delegate changes their mind and votes for the other option + await voting.attemptVoteForMultiple(voteId, VOTE_YEA_VALUE, votersSlice, { from: delegate1 }) + // Check the state after the second vote + votersState = await voting.getVoterStateMultipleAtVote(voteId, votersSlice) + votersState.every((state) => { + assert.equal(state, VOTER_STATE.DELEGATE_YEA, `voter should have 'delegateYea' state`) + }) + vote = await voting.getVote(voteId) + assertBn(vote.yea, votersVotingPowerSum) + assertBn(vote.nay, ZERO_BN) + + // Delegate changes their mind again and votes for the other option during the objection phase + await voting.mockIncreaseTime(VOTING_DURATION - OBJECTION_PHASE_DURATION) + vote = await voting.getVote(voteId) + assert.equal(vote.phase, VOTE_PHASE.OBJECTION) + + await voting.attemptVoteForMultiple(voteId, VOTE_NAY_VALUE, votersSlice, { from: delegate1 }) + // Check the state after the third vote + votersState = await voting.getVoterStateMultipleAtVote(voteId, votersSlice) + votersState.every((state) => { + assert.equal(state, VOTER_STATE.DELEGATE_NAY, `voter should have 'delegateNay' state`) + }) + vote = await voting.getVote(voteId) + assertBn(vote.nay, votersVotingPowerSum) + assertBn(vote.yea, ZERO_BN) + }) + + it(`the new delegate can overwrite the old delegate's vote during the main phase`, async () => { + for (const voter of voters) { + await voting.assignDelegate(delegate1, { from: voter }) + } + + await voting.attemptVoteForMultiple(voteId, VOTE_YEA_VALUE, voters, { from: delegate1 }) + + // Check the state after the first vote + const votersState = await voting.getVoterStateMultipleAtVote(voteId, voters) + assertArraysEqualAsSets(votersState, voters.map(() => VOTER_STATE.DELEGATE_YEA)) + let vote = await voting.getVote(voteId) + const votersVotingPower = await voting.getVotingPowerMultipleAtVote(voteId, voters) + const votersVotingPowerSum = getVotingPowerSum(votersVotingPower) + assertBn(vote.yea, votersVotingPowerSum, 'Vote should have the correct yea value') + + // One of the voters changes their delegate + const voter = voters[0] + await voting.assignDelegate(delegate2, { from: voter }) + + // The new delegate votes for the other option + await voting.attemptVoteFor(voteId, VOTE_NAY_VALUE, voter, { from: delegate2 }) + const voterState = await voting.getVoterState(voteId, voter) + assert.equal(voterState, VOTER_STATE.DELEGATE_NAY) + vote = await voting.getVote(voteId) + assertBn(vote.nay, votersBalances[voter]) + assertBn(vote.yea, votersVotingPowerSum.sub(votersBalances[voter])) + }) + + it(`the new delegate can overwrite the old delegate's vote during the objection phase`, async () => { + for (const voter of voters) { + await voting.assignDelegate(delegate1, { from: voter }) + } + + await voting.attemptVoteForMultiple(voteId, VOTE_YEA_VALUE, voters, { from: delegate1 }) + + // Check the state after the first vote + const votersState = await voting.getVoterStateMultipleAtVote(voteId, voters) + assertArraysEqualAsSets(votersState, voters.map(() => VOTER_STATE.DELEGATE_YEA)) + let vote = await voting.getVote(voteId) + const votersVotingPower = await voting.getVotingPowerMultipleAtVote(voteId, voters) + const votersVotingPowerSum = getVotingPowerSum(votersVotingPower) + assertBn(vote.yea, votersVotingPowerSum, 'Vote should have the correct yea value') + + // Fast-forward the vote to the objection phase + await voting.mockIncreaseTime(VOTING_DURATION - OBJECTION_PHASE_DURATION) + vote = await voting.getVote(voteId) + assert.equal(vote.phase, VOTE_PHASE.OBJECTION) + + // One of the voters changes their delegate + const voter = voters[0] + await voting.assignDelegate(delegate2, { from: voter }) + + // The new delegate votes for the other option + await voting.attemptVoteFor(voteId, VOTE_NAY_VALUE, voter, { from: delegate2 }) + const voterState = await voting.getVoterState(voteId, voter) + assert.equal(voterState, VOTER_STATE.DELEGATE_NAY) + vote = await voting.getVote(voteId) + assertBn(vote.nay, votersBalances[voter]) + assertBn(vote.yea, votersVotingPowerSum.sub(votersBalances[voter])) + }) + + it('A delegate can still vote after being spammed by malisious accounts', async () => { + const [voter1, voter2] = voters.slice(0, 2) + + await voting.assignDelegate(delegate1, { from: voter1 }) for (const holder of spamHolders) { - await token.generateTokens(holder, bigExp(1, decimals)) - await voting.assignDelegate(delegate1, {from: holder}) + await token.generateTokens(holder, bigExp(1, TOKEN_DECIMALS)) + await voting.assignDelegate(delegate1, { from: holder }) + } + + await voting.assignDelegate(delegate1, { from: voter2 }) + + const delegatedVoters = await voting.getDelegatedVoters(delegate1, 0, 600) + assertArraysEqualAsSets(delegatedVoters, [voter1, ...spamHolders, voter2]) + const delegatedVotersVotingPower = await voting.getVotingPowerMultipleAtVote(voteId, delegatedVoters) + const delegatedVotersState = await voting.getVoterStateMultipleAtVote(voteId, delegatedVoters) + + const eligibleDelegatedVoters = [] + for (let i = 0; i < delegatedVoters.length; i++) { + const votingPower = delegatedVotersVotingPower[i] + const voterState = delegatedVotersState[i] + if (votingPower.gt(ZERO_BN) && voterState !== VOTER_STATE.YEA && voterState !== VOTER_STATE.NAY) { + eligibleDelegatedVoters.push(delegatedVoters[i]) + } } + assertArraysEqualAsSets(eligibleDelegatedVoters, [voter1, voter2]) - executionTarget = await ExecutionTarget.new() + await voting.attemptVoteForMultiple(voteId, VOTE_YEA_VALUE, eligibleDelegatedVoters, { from: delegate1 }) + const vote = await voting.getVote(voteId) + const eligibleDelegatedVotersState = await voting.getVoterStateMultipleAtVote(voteId, eligibleDelegatedVoters) + assertArraysEqualAsSets(eligibleDelegatedVotersState, eligibleDelegatedVoters.map(() => VOTER_STATE.DELEGATE_YEA)) + const eligibleDelegatedVotersVotingPower = await voting.getVotingPowerMultipleAtVote(voteId, eligibleDelegatedVoters) + const eligibleDelegatedVotersVotingPowerSum = getVotingPowerSum(eligibleDelegatedVotersVotingPower) + assertBn(vote.yea, eligibleDelegatedVotersVotingPowerSum) + + }) + .timeout(60_000) + + it(`a delegate can't overwrite if voter has voted first`, async () => { + const votersSlice = voters.slice(0, 2) + for (const voter of votersSlice) { + await voting.assignDelegate(delegate1, { from: voter }) + } + + await voting.vote(voteId, VOTE_NAY_VALUE, false, { from: votersSlice[0] }) + + await voting.attemptVoteForMultiple(voteId, VOTE_YEA_VALUE, votersSlice, { from: delegate1 }) + + const selfVoterState = await voting.getVoterState(voteId, votersSlice[0]) + assert.equal(selfVoterState, VOTER_STATE.NAY) + }) + + it(`a delegated voter can't overwrite the "nay" vote of the delegate with "yea" during the objection phase`, async () => { + const voter = voters[0] + await voting.assignDelegate(delegate1, { from: voter }) + + await voting.attemptVoteFor(voteId, VOTE_NAY_VALUE, voter, { from: delegate1 }) + + // Set the time to the start of the objection phase + await voting.mockIncreaseTime(VOTING_DURATION - OBJECTION_PHASE_DURATION) + const { phase } = await voting.getVote(voteId) + assert.equal(phase, VOTE_PHASE.OBJECTION) + + // Can not vote "yea" during the objection phase + await assertRevert( + voting.vote(voteId, VOTE_YEA_VALUE, false, { from: voter }), + ERRORS.VOTING_CAN_NOT_VOTE + ) + }) - const action = {to: executionTarget.address, calldata: executionTarget.contract.methods.execute().encodeABI()} - script = encodeCallScript([action, action]) + }) + + context.skip('Gas estimation tests (should be skipped)', () => { + beforeEach(async () => { + for (const holder of spamHolders) { + await token.generateTokens(holder, bigExp(1, TOKEN_DECIMALS)) + await voting.assignDelegate(delegate1, { from: holder }) + } - const receipt = await voting.methods['newVote(bytes,string)'](script, 'metadata', {from: holder51}) - voteId = getEventArgument(receipt, 'StartVote', 'voteId') - creator = getEventArgument(receipt, 'StartVote', 'creator') - metadata = getEventArgument(receipt, 'StartVote', 'metadata') + voteId = await startEmptyVote() }) it(`voting without delegation`, async () => { const voter = spamHolders[0] - const tx = await voting.vote(voteId, true, false, {from: voter}) + const tx = await voting.vote(voteId, true, false, { from: voter }) console.log('Gas used for a voting without delegation:', tx.receipt.gasUsed) }) it(`voting for 1`, async () => { const voter = spamHolders[0] - const tx = await voting.attemptVoteFor(voteId, false, voter, {from: delegate1}) + const tx = await voting.attemptVoteFor(voteId, false, voter, { from: delegate1 }) console.log('Gas used for voting for 1:', tx.receipt.gasUsed) }) it(`voting for 10`, async () => { - const voters = spamHolders.slice(0, 10) - const tx = await voting.attemptVoteForMultiple(voteId, false, voters, {from: delegate1}) + const votersSlice = spamHolders.slice(0, 10) + const tx = await voting.attemptVoteForMultiple(voteId, false, votersSlice, { from: delegate1 }) console.log('Gas used for voting for 10:', tx.receipt.gasUsed) }) it(`voting for 100`, async () => { - const voters = spamHolders.slice(0, 100) - const tx = await voting.attemptVoteForMultiple(voteId, false, voters, {from: delegate1}) + const votersSlice = spamHolders.slice(0, 100) + const tx = await voting.attemptVoteForMultiple(voteId, false, votersSlice, { from: delegate1 }) console.log('Gas used for voting for 100:', tx.receipt.gasUsed) }) diff --git a/apps/voting/test/helpers/assertArrayAsSets.js b/apps/voting/test/helpers/assertArrayAsSets.js index b837e1705..6a4fb780a 100644 --- a/apps/voting/test/helpers/assertArrayAsSets.js +++ b/apps/voting/test/helpers/assertArrayAsSets.js @@ -14,14 +14,14 @@ function normalizeArg(arg) { return arg; } function assertArraysEqualAsSets(actual, expected, errorMsg) { + assert.equal(actual.length, expected.length, errorMsg || "Arrays do not have the same length."); + actual = actual.map(normalizeArg); expected = expected.map(normalizeArg); const setActual = new Set(actual); const setExpected = new Set(expected); - assert.equal(setActual.size, setExpected.size, errorMsg || "Arrays do not have the same length."); - setActual.forEach(item => { assert.isTrue(setExpected.has(item), errorMsg || "Arrays do not match as sets."); }); @@ -31,4 +31,4 @@ function assertArraysEqualAsSets(actual, expected, errorMsg) { }); } -module.exports = assertArraysEqualAsSets \ No newline at end of file +module.exports = assertArraysEqualAsSets From 714d7c8dfab3fa90bd4c21624a3837244300e1a4 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Thu, 4 Jul 2024 11:17:20 +0200 Subject: [PATCH 087/100] fix: remove unused function --- apps/voting/contracts/Voting.sol | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 4229093b2..a464912fd 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -780,18 +780,6 @@ contract Voting is IForwarder, AragonApp { assert(false); // Should never reach this point } - /** - * @dev Internal function to check if a vote is still open for both support and objection - * @param vote_ The queried vote - * @return True if less than voteTime has passed since the vote start - */ - function _isVoteOpen(Vote storage vote_) internal view returns (bool) { - // The `!vote_.executed` check must stay in place. - // In a particular case `unsafelyChangeVoteTime` call might - // break the vote state and make it possible to vote for the executed vote. - return getTimestamp64() < vote_.startDate.add(voteTime) && !vote_.executed; - } - /** * @dev Calculates whether `_value` is more than a percentage `_pct` of `_total` * @param _value The value to compare From 30c8ccbb81e90df4d85eb04ad259f24929389e4a Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Thu, 4 Jul 2024 11:35:33 +0200 Subject: [PATCH 088/100] fix: remove unreachable code --- apps/voting/contracts/Voting.sol | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index a464912fd..af4e45fa2 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -483,7 +483,7 @@ contract Voting is IForwarder, AragonApp { address[] storage votersList = delegatedVoters[_delegate].addresses; uint256 votersCount = votersList.length; - if (_offset >= votersCount || votersCount == 0) { + if (_offset >= votersCount) { return voters; } @@ -774,10 +774,8 @@ contract Voting is IForwarder, AragonApp { if (timestamp < voteTimeEnd.sub(objectionPhaseTime)) { return VotePhase.Main; } - if (timestamp < voteTimeEnd) { - return VotePhase.Objection; - } - assert(false); // Should never reach this point + assert(timestamp < voteTimeEnd); + return VotePhase.Objection; } /** From 3680b5ac6d7f499a7a586107efffd9438d8244cf Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Thu, 4 Jul 2024 11:39:31 +0200 Subject: [PATCH 089/100] fix: declare _voters.length before loop --- apps/voting/contracts/Voting.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index af4e45fa2..9becfe119 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -298,7 +298,8 @@ contract Voting is IForwarder, AragonApp { bool votedForAtLeastOne = false; uint64 voteSnapshotBlock = vote_.snapshotBlock; - for (uint256 i = 0; i < _voters.length; ++i) { + uint256 votersCount = _voters.length; + for (uint256 i = 0; i < votersCount; ++i) { address voter = _voters[i]; // In version 0.4.24 of Solidity, for view methods, a regular CALL is used instead of STATICCALL. From 50d9802f6e728388b80b275b7baea5d93b9b6b25 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Thu, 4 Jul 2024 11:43:54 +0200 Subject: [PATCH 090/100] docs: fix natspec format --- apps/voting/contracts/Voting.sol | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 9becfe119..7697faf0d 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -417,17 +417,17 @@ contract Voting is IForwarder, AragonApp { /** * @dev Return all information for a vote by its ID * @param _voteId Vote identifier - * @return true if the vote is open - * @return Vote executed status - * @return Vote start date - * @return Vote snapshot block - * @return Vote support required - * @return Vote minimum acceptance quorum - * @return Vote yeas amount - * @return Vote nays amount - * @return Vote power - * @return Vote script - * @return Vote phase + * @return open True if the vote is open + * @return executed Vote executed status + * @return startDate Vote start date + * @return snapshotBlock Vote snapshot block + * @return supportRequired Vote support required + * @return minAcceptQuorum Vote minimum acceptance quorum + * @return yea Vote yeas amount + * @return nay Vote nays amount + * @return votingPower Vote power + * @return script Vote script + * @return phase Vote phase */ function getVote(uint256 _voteId) public @@ -477,7 +477,7 @@ contract Voting is IForwarder, AragonApp { * @param _delegate Address of the delegate * @param _offset Number of delegated voters from the start of the list to skip * @param _limit Maximum number of delegated voters to return. The length of the returned slice can be lower than if the end of the list is reached - * @return Array of delegated voters' addresses + * @return voters Array of delegated voters' addresses */ function getDelegatedVoters(address _delegate, uint256 _offset, uint256 _limit) external view returns (address[] memory voters) { require(_delegate != address(0), ERROR_ZERO_ADDRESS_PASSED); @@ -518,7 +518,7 @@ contract Voting is IForwarder, AragonApp { * @notice Return the list of `VoterState` for the `_voters` for the vote #`_voteId` * @param _voteId Vote identifier * @param _voters List of voters - * @return Array of voter states + * @return voterStatesList Array of voter states */ function getVoterStateMultipleAtVote(uint256 _voteId, address[] _voters) external view voteExists(_voteId) returns (VoterState[] memory voterStatesList) { uint256 votersCount = _voters.length; @@ -532,7 +532,7 @@ contract Voting is IForwarder, AragonApp { /** * @notice Return the cumulative voting power of the `_voters` at the current block * @param _voters List of voters - * @return Array of governance token balances + * @return balances Array of governance token balances */ function getVotingPowerMultiple(address[] _voters) external view returns (uint256[] memory balances) { return _getVotingPowerMultipleAt(_voters, getBlockNumber64()); @@ -542,7 +542,7 @@ contract Voting is IForwarder, AragonApp { * @notice Return the cumulative voting power of the `_voters` at the vote #`_voteId` snapshot block * @param _voteId Vote identifier * @param _voters List of voters - * @return Array of governance token balances + * @return balances Array of governance token balances */ function getVotingPowerMultipleAtVote(uint256 _voteId, address[] _voters) external view voteExists(_voteId) returns (uint256[] memory balances) { return _getVotingPowerMultipleAt(_voters, votes[_voteId].snapshotBlock); From c112cc2eae0ad4b87bc4877a705104b4ade385df Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Thu, 11 Jul 2024 18:33:16 +0200 Subject: [PATCH 091/100] docs: update canVote comment --- apps/voting/contracts/Voting.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/voting/contracts/Voting.sol b/apps/voting/contracts/Voting.sol index 7697faf0d..4f3e40d60 100644 --- a/apps/voting/contracts/Voting.sol +++ b/apps/voting/contracts/Voting.sol @@ -394,7 +394,7 @@ contract Voting is IForwarder, AragonApp { * created via `newVote(),` which requires initialization * @param _voteId Vote identifier * @param _voter Address of the voter - * @return True if the given voter can participate in the main phase of a certain vote + * @return True if the given voter can participate in main or objection phase of the vote * both directly and indirectly by a delegate voting for them, false otherwise */ function canVote(uint256 _voteId, address _voter) external view voteExists(_voteId) returns (bool) { From 0ec288b4ec5b06a2ad0374633aa9531f83a17e98 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Wed, 16 Oct 2024 17:03:35 +0200 Subject: [PATCH 092/100] fix: revert yarn.lock changes --- yarn.lock | 1545 ++++++++++++----------------------------------------- 1 file changed, 337 insertions(+), 1208 deletions(-) diff --git a/yarn.lock b/yarn.lock index 8af9fda56..7f9b50f4d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,11 +2,6 @@ # yarn lockfile v1 -"@adraffy/ens-normalize@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz#d2a39395c587e092d77cbbc80acf956a54f38bf7" - integrity sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q== - "@aragon/contract-helpers-test@0.1.0", "@aragon/contract-helpers-test@^0.1.0": version "0.1.0" resolved "https://registry.yarnpkg.com/@aragon/contract-helpers-test/-/contract-helpers-test-0.1.0.tgz#5c5f09739a0b33ab66843bc4849cb2b997d88af0" @@ -226,10 +221,30 @@ "@babel/helper-validator-identifier" "^7.14.9" to-fast-properties "^2.0.0" -"@colors/colors@1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" - integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== +"@ethereumjs/block@^3.4.0", "@ethereumjs/block@^3.5.0", "@ethereumjs/block@^3.5.1": + version "3.5.1" + resolved "https://registry.yarnpkg.com/@ethereumjs/block/-/block-3.5.1.tgz#59737d393503249aa750c37dfc83896234f4e175" + integrity sha512-MoY9bHKABOBK6BW0v1N1Oc0Cve4x/giX67M3TtrVBUsKQTj2eznLGKpydoitxWSZ+WgKKSVhfRMzbCGRwk7T5w== + dependencies: + "@ethereumjs/common" "^2.5.0" + "@ethereumjs/tx" "^3.3.1" + ethereumjs-util "^7.1.1" + merkle-patricia-tree "^4.2.1" + +"@ethereumjs/blockchain@^5.4.0", "@ethereumjs/blockchain@^5.4.1": + version "5.4.2" + resolved "https://registry.yarnpkg.com/@ethereumjs/blockchain/-/blockchain-5.4.2.tgz#5074e0a0157818762a5f5175ea0bd93c5455fe32" + integrity sha512-AOAAwz/lw2lciG9gf5wHi7M/qknraXXnLR66lYgbQ04qfyFC3ZE5x/5rLVm1Vu+kfJLlKrYZTmA0IbOkc7kvgw== + dependencies: + "@ethereumjs/block" "^3.5.1" + "@ethereumjs/common" "^2.5.0" + "@ethereumjs/ethash" "^1.1.0" + debug "^2.2.0" + ethereumjs-util "^7.1.1" + level-mem "^5.0.1" + lru-cache "^5.1.1" + rlp "^2.2.4" + semaphore-async-await "^1.5.1" "@ethereumjs/common@^2.3.0", "@ethereumjs/common@^2.4.0", "@ethereumjs/common@^2.5.0": version "2.5.0" @@ -247,12 +262,18 @@ crc-32 "^1.2.0" ethereumjs-util "^7.1.4" -"@ethereumjs/rlp@^4.0.1": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@ethereumjs/rlp/-/rlp-4.0.1.tgz#626fabfd9081baab3d0a3074b0c7ecaf674aaa41" - integrity sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw== +"@ethereumjs/ethash@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/ethash/-/ethash-1.1.0.tgz#7c5918ffcaa9cb9c1dc7d12f77ef038c11fb83fb" + integrity sha512-/U7UOKW6BzpA+Vt+kISAoeDie1vAvY4Zy2KF5JJb+So7+1yKmJeJEHOGSnQIj330e9Zyl3L5Nae6VZyh2TJnAA== + dependencies: + "@ethereumjs/block" "^3.5.0" + "@types/levelup" "^4.3.0" + buffer-xor "^2.0.1" + ethereumjs-util "^7.1.1" + miller-rabin "^4.0.0" -"@ethereumjs/tx@^3.2.1", "@ethereumjs/tx@^3.3.0": +"@ethereumjs/tx@^3.2.1", "@ethereumjs/tx@^3.3.0", "@ethereumjs/tx@^3.3.1": version "3.3.2" resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-3.3.2.tgz#348d4624bf248aaab6c44fec2ae67265efe3db00" integrity sha512-6AaJhwg4ucmwTvw/1qLaZUX5miWrwZ4nLOUsKyb/HtzS3BMw/CasKhdi1ims9mBKeK9sOJCH4qGKOBGyJCeeog== @@ -268,14 +289,24 @@ "@ethereumjs/common" "^2.6.3" ethereumjs-util "^7.1.4" -"@ethereumjs/util@^8.1.0": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@ethereumjs/util/-/util-8.1.0.tgz#299df97fb6b034e0577ce9f94c7d9d1004409ed4" - integrity sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA== +"@ethereumjs/vm@^5.5.2": + version "5.5.3" + resolved "https://registry.yarnpkg.com/@ethereumjs/vm/-/vm-5.5.3.tgz#dc8b30dd35efb589db093592600207660fa8dada" + integrity sha512-0k5OreWnlgXYs54wohgO11jtGI05GDasj2EYxzuaStxTi15CS3vow5wGYELC1pG9xngE1F/mFmKi/f14XRuDow== dependencies: - "@ethereumjs/rlp" "^4.0.1" - ethereum-cryptography "^2.0.0" - micro-ftch "^0.3.1" + "@ethereumjs/block" "^3.5.0" + "@ethereumjs/blockchain" "^5.4.1" + "@ethereumjs/common" "^2.5.0" + "@ethereumjs/tx" "^3.3.1" + async-eventemitter "^0.2.4" + core-js-pure "^3.0.1" + debug "^2.2.0" + ethereumjs-util "^7.1.1" + functional-red-black-tree "^1.0.1" + mcl-wasm "^0.7.1" + merkle-patricia-tree "^4.2.1" + rustbn.js "~0.2.0" + util.promisify "^1.0.1" "@ethersproject/abi@5.0.0-beta.153": version "5.0.0-beta.153" @@ -322,21 +353,6 @@ "@ethersproject/properties" "^5.5.0" "@ethersproject/strings" "^5.5.0" -"@ethersproject/abi@^5.0.9", "@ethersproject/abi@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" - integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== - dependencies: - "@ethersproject/address" "^5.7.0" - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/constants" "^5.7.0" - "@ethersproject/hash" "^5.7.0" - "@ethersproject/keccak256" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/strings" "^5.7.0" - "@ethersproject/abstract-provider@5.5.1", "@ethersproject/abstract-provider@^5.5.0": version "5.5.1" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.5.1.tgz#2f1f6e8a3ab7d378d8ad0b5718460f85649710c5" @@ -350,19 +366,6 @@ "@ethersproject/transactions" "^5.5.0" "@ethersproject/web" "^5.5.0" -"@ethersproject/abstract-provider@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz#b0a8550f88b6bf9d51f90e4795d48294630cb9ef" - integrity sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw== - dependencies: - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/networks" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/transactions" "^5.7.0" - "@ethersproject/web" "^5.7.0" - "@ethersproject/abstract-signer@5.5.0", "@ethersproject/abstract-signer@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.5.0.tgz#590ff6693370c60ae376bf1c7ada59eb2a8dd08d" @@ -374,17 +377,6 @@ "@ethersproject/logger" "^5.5.0" "@ethersproject/properties" "^5.5.0" -"@ethersproject/abstract-signer@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz#13f4f32117868452191a4649723cb086d2b596b2" - integrity sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ== - dependencies: - "@ethersproject/abstract-provider" "^5.7.0" - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/address@5.5.0", "@ethersproject/address@>=5.0.0-beta.128", "@ethersproject/address@^5.0.2", "@ethersproject/address@^5.0.4", "@ethersproject/address@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.5.0.tgz#bcc6f576a553f21f3dd7ba17248f81b473c9c78f" @@ -396,17 +388,6 @@ "@ethersproject/logger" "^5.5.0" "@ethersproject/rlp" "^5.5.0" -"@ethersproject/address@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37" - integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA== - dependencies: - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/keccak256" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/rlp" "^5.7.0" - "@ethersproject/base64@5.5.0", "@ethersproject/base64@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.5.0.tgz#881e8544e47ed976930836986e5eb8fab259c090" @@ -414,13 +395,6 @@ dependencies: "@ethersproject/bytes" "^5.5.0" -"@ethersproject/base64@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.7.0.tgz#ac4ee92aa36c1628173e221d0d01f53692059e1c" - integrity sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ== - dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/basex@5.5.0", "@ethersproject/basex@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.5.0.tgz#e40a53ae6d6b09ab4d977bd037010d4bed21b4d3" @@ -438,15 +412,6 @@ "@ethersproject/logger" "^5.5.0" bn.js "^4.11.9" -"@ethersproject/bignumber@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.7.0.tgz#e2f03837f268ba655ffba03a57853e18a18dc9c2" - integrity sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw== - dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - bn.js "^5.2.1" - "@ethersproject/bytes@5.5.0", "@ethersproject/bytes@>=5.0.0-beta.129", "@ethersproject/bytes@^5.0.4", "@ethersproject/bytes@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.5.0.tgz#cb11c526de657e7b45d2e0f0246fb3b9d29a601c" @@ -454,13 +419,6 @@ dependencies: "@ethersproject/logger" "^5.5.0" -"@ethersproject/bytes@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d" - integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A== - dependencies: - "@ethersproject/logger" "^5.7.0" - "@ethersproject/constants@5.5.0", "@ethersproject/constants@>=5.0.0-beta.128", "@ethersproject/constants@^5.0.4", "@ethersproject/constants@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.5.0.tgz#d2a2cd7d94bd1d58377d1d66c4f53c9be4d0a45e" @@ -468,13 +426,6 @@ dependencies: "@ethersproject/bignumber" "^5.5.0" -"@ethersproject/constants@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.7.0.tgz#df80a9705a7e08984161f09014ea012d1c75295e" - integrity sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA== - dependencies: - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/contracts@5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.5.0.tgz#b735260d4bd61283a670a82d5275e2a38892c197" @@ -505,21 +456,6 @@ "@ethersproject/properties" "^5.5.0" "@ethersproject/strings" "^5.5.0" -"@ethersproject/hash@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7" - integrity sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g== - dependencies: - "@ethersproject/abstract-signer" "^5.7.0" - "@ethersproject/address" "^5.7.0" - "@ethersproject/base64" "^5.7.0" - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/keccak256" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/strings" "^5.7.0" - "@ethersproject/hdnode@5.5.0", "@ethersproject/hdnode@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.5.0.tgz#4a04e28f41c546f7c978528ea1575206a200ddf6" @@ -565,24 +501,11 @@ "@ethersproject/bytes" "^5.5.0" js-sha3 "0.8.0" -"@ethersproject/keccak256@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.7.0.tgz#3186350c6e1cd6aba7940384ec7d6d9db01f335a" - integrity sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg== - dependencies: - "@ethersproject/bytes" "^5.7.0" - js-sha3 "0.8.0" - "@ethersproject/logger@5.5.0", "@ethersproject/logger@>=5.0.0-beta.129", "@ethersproject/logger@^5.0.5", "@ethersproject/logger@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.5.0.tgz#0c2caebeff98e10aefa5aef27d7441c7fd18cf5d" integrity sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg== -"@ethersproject/logger@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892" - integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig== - "@ethersproject/networks@5.5.0", "@ethersproject/networks@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.5.0.tgz#babec47cab892c51f8dd652ce7f2e3e14283981a" @@ -590,13 +513,6 @@ dependencies: "@ethersproject/logger" "^5.5.0" -"@ethersproject/networks@^5.7.0": - version "5.7.1" - resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.7.1.tgz#118e1a981d757d45ccea6bb58d9fd3d9db14ead6" - integrity sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ== - dependencies: - "@ethersproject/logger" "^5.7.0" - "@ethersproject/pbkdf2@5.5.0", "@ethersproject/pbkdf2@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.5.0.tgz#e25032cdf02f31505d47afbf9c3e000d95c4a050" @@ -612,13 +528,6 @@ dependencies: "@ethersproject/logger" "^5.5.0" -"@ethersproject/properties@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.7.0.tgz#a6e12cb0439b878aaf470f1902a176033067ed30" - integrity sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw== - dependencies: - "@ethersproject/logger" "^5.7.0" - "@ethersproject/providers@5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.5.0.tgz#bc2876a8fe5e0053ed9828b1f3767ae46e43758b" @@ -660,14 +569,6 @@ "@ethersproject/bytes" "^5.5.0" "@ethersproject/logger" "^5.5.0" -"@ethersproject/rlp@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.7.0.tgz#de39e4d5918b9d74d46de93af80b7685a9c21304" - integrity sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w== - dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/sha2@5.5.0", "@ethersproject/sha2@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.5.0.tgz#a40a054c61f98fd9eee99af2c3cc6ff57ec24db7" @@ -689,18 +590,6 @@ elliptic "6.5.4" hash.js "1.1.7" -"@ethersproject/signing-key@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.7.0.tgz#06b2df39411b00bc57c7c09b01d1e41cf1b16ab3" - integrity sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q== - dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - bn.js "^5.2.1" - elliptic "6.5.4" - hash.js "1.1.7" - "@ethersproject/solidity@5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.5.0.tgz#2662eb3e5da471b85a20531e420054278362f93f" @@ -722,15 +611,6 @@ "@ethersproject/constants" "^5.5.0" "@ethersproject/logger" "^5.5.0" -"@ethersproject/strings@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.7.0.tgz#54c9d2a7c57ae8f1205c88a9d3a56471e14d5ed2" - integrity sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg== - dependencies: - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/constants" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/transactions@5.5.0", "@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.5.0.tgz#7e9bf72e97bcdf69db34fe0d59e2f4203c7a2908" @@ -746,21 +626,6 @@ "@ethersproject/rlp" "^5.5.0" "@ethersproject/signing-key" "^5.5.0" -"@ethersproject/transactions@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.7.0.tgz#91318fc24063e057885a6af13fdb703e1f993d3b" - integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ== - dependencies: - "@ethersproject/address" "^5.7.0" - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/constants" "^5.7.0" - "@ethersproject/keccak256" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/rlp" "^5.7.0" - "@ethersproject/signing-key" "^5.7.0" - "@ethersproject/units@5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.5.0.tgz#104d02db5b5dc42cc672cc4587bafb87a95ee45e" @@ -770,15 +635,6 @@ "@ethersproject/constants" "^5.5.0" "@ethersproject/logger" "^5.5.0" -"@ethersproject/units@^5.7.0": - version "5.7.0" - resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.7.0.tgz#637b563d7e14f42deeee39245275d477aae1d8b1" - integrity sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg== - dependencies: - "@ethersproject/bignumber" "^5.7.0" - "@ethersproject/constants" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/wallet@5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.5.0.tgz#322a10527a440ece593980dca6182f17d54eae75" @@ -811,17 +667,6 @@ "@ethersproject/properties" "^5.5.0" "@ethersproject/strings" "^5.5.0" -"@ethersproject/web@^5.7.0": - version "5.7.1" - resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.7.1.tgz#de1f285b373149bee5928f4eb7bcb87ee5fbb4ae" - integrity sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w== - dependencies: - "@ethersproject/base64" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/logger" "^5.7.0" - "@ethersproject/properties" "^5.7.0" - "@ethersproject/strings" "^5.7.0" - "@ethersproject/wordlists@5.5.0", "@ethersproject/wordlists@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.5.0.tgz#aac74963aa43e643638e5172353d931b347d584f" @@ -907,23 +752,6 @@ unique-filename "^1.1.1" which "^1.3.1" -"@fastify/busboy@^2.0.0": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d" - integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA== - -"@isaacs/cliui@^8.0.2": - version "8.0.2" - resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" - integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== - dependencies: - string-width "^5.1.2" - string-width-cjs "npm:string-width@^4.2.0" - strip-ansi "^7.0.1" - strip-ansi-cjs "npm:strip-ansi@^6.0.1" - wrap-ansi "^8.1.0" - wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" - "@lerna/add@3.21.0": version "3.21.0" resolved "https://registry.yarnpkg.com/@lerna/add/-/add-3.21.0.tgz#27007bde71cc7b0a2969ab3c2f0ae41578b4577b" @@ -1609,17 +1437,6 @@ npmlog "^4.1.2" write-file-atomic "^2.3.0" -"@metamask/eth-sig-util@^4.0.0": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz#3ad61f6ea9ad73ba5b19db780d40d9aae5157088" - integrity sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ== - dependencies: - ethereumjs-abi "^0.6.8" - ethereumjs-util "^6.2.1" - ethjs-util "^0.1.6" - tweetnacl "^1.0.3" - tweetnacl-util "^0.15.1" - "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" @@ -1633,40 +1450,6 @@ resolved "https://registry.yarnpkg.com/@multiformats/base-x/-/base-x-4.0.1.tgz#95ff0fa58711789d53aefb2590a8b7a4e715d121" integrity sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw== -"@noble/curves@1.2.0", "@noble/curves@~1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35" - integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw== - dependencies: - "@noble/hashes" "1.3.2" - -"@noble/curves@1.3.0", "@noble/curves@~1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.3.0.tgz#01be46da4fd195822dab821e72f71bf4aeec635e" - integrity sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA== - dependencies: - "@noble/hashes" "1.3.3" - -"@noble/hashes@1.2.0", "@noble/hashes@~1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12" - integrity sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ== - -"@noble/hashes@1.3.2": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" - integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== - -"@noble/hashes@1.3.3", "@noble/hashes@~1.3.0", "@noble/hashes@~1.3.2": - version "1.3.3" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" - integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== - -"@noble/secp256k1@1.7.1", "@noble/secp256k1@~1.7.0": - version "1.7.1" - resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" - integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw== - "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -1693,157 +1476,6 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" -"@nomicfoundation/edr-darwin-arm64@0.3.5": - version "0.3.5" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.3.5.tgz#3c428000ec27501617a00f7c447054a272f99177" - integrity sha512-gIXUIiPMUy6roLHpNlxf15DumU7/YhffUf7XIB+WUjMecaySfTGyZsTGnCMJZqrDyiYqWPyPKwCV/2u/jqFAUg== - -"@nomicfoundation/edr-darwin-x64@0.3.5": - version "0.3.5" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.3.5.tgz#fee26c4a83c9fc534bc0a719e88382fafeed69fe" - integrity sha512-0MrpOCXUK8gmplpYZ2Cy0holHEylvWoNeecFcrP2WJ5DLQzrB23U5JU2MvUzOJ7aL76Za1VXNBWi/UeTWdHM+w== - -"@nomicfoundation/edr-linux-arm64-gnu@0.3.5": - version "0.3.5" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.3.5.tgz#c48ad44b578abb50706cd8cad607a65b1289689f" - integrity sha512-aw9f7AZMiY1dZFNePJGKho2k+nEgFgzUAyyukiKfSqUIMXoFXMf1U3Ujv848czrSq9c5XGcdDa2xnEf3daU3xg== - -"@nomicfoundation/edr-linux-arm64-musl@0.3.5": - version "0.3.5" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.3.5.tgz#7cc1b12e2adf872e5a542647182262cc4582f8d9" - integrity sha512-cVFRQjyABBlsbDj+XTczYBfrCHprZ6YNzN8gGGSqAh+UGIJkAIRomK6ar27GyJLNx3HkgbuDoi/9kA0zOo/95w== - -"@nomicfoundation/edr-linux-x64-gnu@0.3.5": - version "0.3.5" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.3.5.tgz#ae4366c6fad03ea6ec3e2f2bafe397661c11f054" - integrity sha512-CjOg85DfR1Vt0fQWn5U0qi26DATK9tVzo3YOZEyI0JBsnqvk43fUTPv3uUAWBrPIRg5O5kOc9xG13hSpCBBxBg== - -"@nomicfoundation/edr-linux-x64-musl@0.3.5": - version "0.3.5" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.3.5.tgz#4071929fcece90f95c271f6ba455c55f35750efd" - integrity sha512-hvX8bBGpBydAVevzK8jsu2FlqVZK1RrCyTX6wGHnltgMuBaoGLHYtNHiFpteOaJw2byYMiORc2bvj+98LhJ0Ew== - -"@nomicfoundation/edr-win32-x64-msvc@0.3.5": - version "0.3.5" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.3.5.tgz#6e6684e56f336c67fbda9bf75b21d7ac586631b8" - integrity sha512-IJXjW13DY5UPsx/eG5DGfXtJ7Ydwrvw/BTZ2Y93lRLHzszVpSmeVmlxjZP5IW2afTSgMLaAAsqNw4NhppRGN8A== - -"@nomicfoundation/edr@^0.3.5": - version "0.3.5" - resolved "https://registry.yarnpkg.com/@nomicfoundation/edr/-/edr-0.3.5.tgz#04f980386f871e1c3bbc180b290d37af0336c81d" - integrity sha512-dPSM9DuI1sr71gqWUMgLo8MjHQWO4+WNDm3iWaT6P4vUFJReZX5qwA5X+3UwIPBry8GvNY084u7yWUvB3/8rqA== - optionalDependencies: - "@nomicfoundation/edr-darwin-arm64" "0.3.5" - "@nomicfoundation/edr-darwin-x64" "0.3.5" - "@nomicfoundation/edr-linux-arm64-gnu" "0.3.5" - "@nomicfoundation/edr-linux-arm64-musl" "0.3.5" - "@nomicfoundation/edr-linux-x64-gnu" "0.3.5" - "@nomicfoundation/edr-linux-x64-musl" "0.3.5" - "@nomicfoundation/edr-win32-x64-msvc" "0.3.5" - -"@nomicfoundation/ethereumjs-common@4.0.4": - version "4.0.4" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-4.0.4.tgz#9901f513af2d4802da87c66d6f255b510bef5acb" - integrity sha512-9Rgb658lcWsjiicr5GzNCjI1llow/7r0k50dLL95OJ+6iZJcVbi15r3Y0xh2cIO+zgX0WIHcbzIu6FeQf9KPrg== - dependencies: - "@nomicfoundation/ethereumjs-util" "9.0.4" - -"@nomicfoundation/ethereumjs-rlp@5.0.4": - version "5.0.4" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-5.0.4.tgz#66c95256fc3c909f6fb18f6a586475fc9762fa30" - integrity sha512-8H1S3s8F6QueOc/X92SdrA4RDenpiAEqMg5vJH99kcQaCy/a3Q6fgseo75mgWlbanGJXSlAPtnCeG9jvfTYXlw== - -"@nomicfoundation/ethereumjs-tx@5.0.4": - version "5.0.4" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-5.0.4.tgz#b0ceb58c98cc34367d40a30d255d6315b2f456da" - integrity sha512-Xjv8wAKJGMrP1f0n2PeyfFCCojHd7iS3s/Ab7qzF1S64kxZ8Z22LCMynArYsVqiFx6rzYy548HNVEyI+AYN/kw== - dependencies: - "@nomicfoundation/ethereumjs-common" "4.0.4" - "@nomicfoundation/ethereumjs-rlp" "5.0.4" - "@nomicfoundation/ethereumjs-util" "9.0.4" - ethereum-cryptography "0.1.3" - -"@nomicfoundation/ethereumjs-util@9.0.4": - version "9.0.4" - resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-9.0.4.tgz#84c5274e82018b154244c877b76bc049a4ed7b38" - integrity sha512-sLOzjnSrlx9Bb9EFNtHzK/FJFsfg2re6bsGqinFinH1gCqVfz9YYlXiMWwDM4C/L4ywuHFCYwfKTVr/QHQcU0Q== - dependencies: - "@nomicfoundation/ethereumjs-rlp" "5.0.4" - ethereum-cryptography "0.1.3" - -"@nomicfoundation/hardhat-network-helpers@^1.0.10": - version "1.0.10" - resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-network-helpers/-/hardhat-network-helpers-1.0.10.tgz#c61042ceb104fdd6c10017859fdef6529c1d6585" - integrity sha512-R35/BMBlx7tWN5V6d/8/19QCwEmIdbnA4ZrsuXgvs8i2qFx5i7h6mH5pBS4Pwi4WigLH+upl6faYusrNPuzMrQ== - dependencies: - ethereumjs-util "^7.1.4" - -"@nomicfoundation/solidity-analyzer-darwin-arm64@0.1.1": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.1.tgz#4c858096b1c17fe58a474fe81b46815f93645c15" - integrity sha512-KcTodaQw8ivDZyF+D76FokN/HdpgGpfjc/gFCImdLUyqB6eSWVaZPazMbeAjmfhx3R0zm/NYVzxwAokFKgrc0w== - -"@nomicfoundation/solidity-analyzer-darwin-x64@0.1.1": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-darwin-x64/-/solidity-analyzer-darwin-x64-0.1.1.tgz#6e25ccdf6e2d22389c35553b64fe6f3fdaec432c" - integrity sha512-XhQG4BaJE6cIbjAVtzGOGbK3sn1BO9W29uhk9J8y8fZF1DYz0Doj8QDMfpMu+A6TjPDs61lbsmeYodIDnfveSA== - -"@nomicfoundation/solidity-analyzer-freebsd-x64@0.1.1": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-freebsd-x64/-/solidity-analyzer-freebsd-x64-0.1.1.tgz#0a224ea50317139caeebcdedd435c28a039d169c" - integrity sha512-GHF1VKRdHW3G8CndkwdaeLkVBi5A9u2jwtlS7SLhBc8b5U/GcoL39Q+1CSO3hYqePNP+eV5YI7Zgm0ea6kMHoA== - -"@nomicfoundation/solidity-analyzer-linux-arm64-gnu@0.1.1": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-arm64-gnu/-/solidity-analyzer-linux-arm64-gnu-0.1.1.tgz#dfa085d9ffab9efb2e7b383aed3f557f7687ac2b" - integrity sha512-g4Cv2fO37ZsUENQ2vwPnZc2zRenHyAxHcyBjKcjaSmmkKrFr64yvzeNO8S3GBFCo90rfochLs99wFVGT/0owpg== - -"@nomicfoundation/solidity-analyzer-linux-arm64-musl@0.1.1": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-arm64-musl/-/solidity-analyzer-linux-arm64-musl-0.1.1.tgz#c9e06b5d513dd3ab02a7ac069c160051675889a4" - integrity sha512-WJ3CE5Oek25OGE3WwzK7oaopY8xMw9Lhb0mlYuJl/maZVo+WtP36XoQTb7bW/i8aAdHW5Z+BqrHMux23pvxG3w== - -"@nomicfoundation/solidity-analyzer-linux-x64-gnu@0.1.1": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-x64-gnu/-/solidity-analyzer-linux-x64-gnu-0.1.1.tgz#8d328d16839e52571f72f2998c81e46bf320f893" - integrity sha512-5WN7leSr5fkUBBjE4f3wKENUy9HQStu7HmWqbtknfXkkil+eNWiBV275IOlpXku7v3uLsXTOKpnnGHJYI2qsdA== - -"@nomicfoundation/solidity-analyzer-linux-x64-musl@0.1.1": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-x64-musl/-/solidity-analyzer-linux-x64-musl-0.1.1.tgz#9b49d0634b5976bb5ed1604a1e1b736f390959bb" - integrity sha512-KdYMkJOq0SYPQMmErv/63CwGwMm5XHenEna9X9aB8mQmhDBrYrlAOSsIPgFCUSL0hjxE3xHP65/EPXR/InD2+w== - -"@nomicfoundation/solidity-analyzer-win32-arm64-msvc@0.1.1": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-win32-arm64-msvc/-/solidity-analyzer-win32-arm64-msvc-0.1.1.tgz#e2867af7264ebbcc3131ef837878955dd6a3676f" - integrity sha512-VFZASBfl4qiBYwW5xeY20exWhmv6ww9sWu/krWSesv3q5hA0o1JuzmPHR4LPN6SUZj5vcqci0O6JOL8BPw+APg== - -"@nomicfoundation/solidity-analyzer-win32-ia32-msvc@0.1.1": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-win32-ia32-msvc/-/solidity-analyzer-win32-ia32-msvc-0.1.1.tgz#0685f78608dd516c8cdfb4896ed451317e559585" - integrity sha512-JnFkYuyCSA70j6Si6cS1A9Gh1aHTEb8kOTBApp/c7NRTFGNMH8eaInKlyuuiIbvYFhlXW4LicqyYuWNNq9hkpQ== - -"@nomicfoundation/solidity-analyzer-win32-x64-msvc@0.1.1": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-win32-x64-msvc/-/solidity-analyzer-win32-x64-msvc-0.1.1.tgz#c9a44f7108646f083b82e851486e0f6aeb785836" - integrity sha512-HrVJr6+WjIXGnw3Q9u6KQcbZCtk0caVWhCdFADySvRyUxJ8PnzlaP+MhwNE8oyT8OZ6ejHBRrrgjSqDCFXGirw== - -"@nomicfoundation/solidity-analyzer@^0.1.0": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.1.tgz#f5f4d36d3f66752f59a57e7208cd856f3ddf6f2d" - integrity sha512-1LMtXj1puAxyFusBgUIy5pZk3073cNXYnXUpuNKFghHbIit/xZgbk0AokpUADbNm3gyD6bFWl3LRFh3dhVdREg== - optionalDependencies: - "@nomicfoundation/solidity-analyzer-darwin-arm64" "0.1.1" - "@nomicfoundation/solidity-analyzer-darwin-x64" "0.1.1" - "@nomicfoundation/solidity-analyzer-freebsd-x64" "0.1.1" - "@nomicfoundation/solidity-analyzer-linux-arm64-gnu" "0.1.1" - "@nomicfoundation/solidity-analyzer-linux-arm64-musl" "0.1.1" - "@nomicfoundation/solidity-analyzer-linux-x64-gnu" "0.1.1" - "@nomicfoundation/solidity-analyzer-linux-x64-musl" "0.1.1" - "@nomicfoundation/solidity-analyzer-win32-arm64-msvc" "0.1.1" - "@nomicfoundation/solidity-analyzer-win32-ia32-msvc" "0.1.1" - "@nomicfoundation/solidity-analyzer-win32-x64-msvc" "0.1.1" - "@nomiclabs/buidler-ganache@^1.3.3": version "1.3.3" resolved "https://registry.yarnpkg.com/@nomiclabs/buidler-ganache/-/buidler-ganache-1.3.3.tgz#220beb381b0df45bf1860396df4a639267711066" @@ -1947,21 +1579,18 @@ resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.0.2.tgz#c472abcba0c5185aaa4ad4070146e95213c68511" integrity sha512-6quxWe8wwS4X5v3Au8q1jOvXYEPkS1Fh+cME5u6AwNdnI4uERvPlVjlgRWzpnb+Rrt1l/cEqiNRH9GlsBMSDQg== -"@nomiclabs/hardhat-etherscan@^3.1.8": - version "3.1.8" - resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-3.1.8.tgz#3c12ee90b3733e0775e05111146ef9418d4f5a38" - integrity sha512-v5F6IzQhrsjHh6kQz4uNrym49brK9K5bYCq2zQZ729RYRaifI9hHbtmK+KkIVevfhut7huQFEQ77JLRMAzWYjQ== +"@nomiclabs/hardhat-etherscan@^2.1.6": + version "2.1.6" + resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-2.1.6.tgz#8d1502f42adc6f7b8ef16fb917c0b5a8780cb83a" + integrity sha512-gCvT5fj8GbXS9+ACS3BzrX0pzYHHZqAHCb+NcipOkl2cy48FakUXlzrCf4P4sTH+Y7W10OgT62ezD1sJ+/NikQ== dependencies: "@ethersproject/abi" "^5.1.2" "@ethersproject/address" "^5.0.2" - cbor "^8.1.0" - chalk "^2.4.2" + cbor "^5.0.2" debug "^4.1.1" fs-extra "^7.0.1" - lodash "^4.17.11" + node-fetch "^2.6.0" semver "^6.3.0" - table "^6.8.0" - undici "^5.14.0" "@nomiclabs/hardhat-ganache@^2.0.1": version "2.0.1" @@ -2117,11 +1746,6 @@ dependencies: "@octokit/openapi-types" "^11.2.0" -"@pkgjs/parseargs@^0.11.0": - version "0.11.0" - resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" - integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== - "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" @@ -2209,62 +1833,6 @@ debug "^3.1.0" hosted-git-info "^2.6.0" -"@scure/base@~1.1.0", "@scure/base@~1.1.2", "@scure/base@~1.1.4": - version "1.1.6" - resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.6.tgz#8ce5d304b436e4c84f896e0550c83e4d88cb917d" - integrity sha512-ok9AWwhcgYuGG3Zfhyqg+zwl+Wn5uE+dwC0NV/2qQkx4dABbb/bx96vWu8NSj+BNjjSjno+JRYRjle1jV08k3g== - -"@scure/bip32@1.1.5": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.1.5.tgz#d2ccae16dcc2e75bc1d75f5ef3c66a338d1ba300" - integrity sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw== - dependencies: - "@noble/hashes" "~1.2.0" - "@noble/secp256k1" "~1.7.0" - "@scure/base" "~1.1.0" - -"@scure/bip32@1.3.2": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.2.tgz#90e78c027d5e30f0b22c1f8d50ff12f3fb7559f8" - integrity sha512-N1ZhksgwD3OBlwTv3R6KFEcPojl/W4ElJOeCZdi+vuI5QmTFwLq3OFf2zd2ROpKvxFdgZ6hUpb0dx9bVNEwYCA== - dependencies: - "@noble/curves" "~1.2.0" - "@noble/hashes" "~1.3.2" - "@scure/base" "~1.1.2" - -"@scure/bip32@1.3.3": - version "1.3.3" - resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.3.tgz#a9624991dc8767087c57999a5d79488f48eae6c8" - integrity sha512-LJaN3HwRbfQK0X1xFSi0Q9amqOgzQnnDngIt+ZlsBC3Bm7/nE7K0kwshZHyaru79yIVRv/e1mQAjZyuZG6jOFQ== - dependencies: - "@noble/curves" "~1.3.0" - "@noble/hashes" "~1.3.2" - "@scure/base" "~1.1.4" - -"@scure/bip39@1.1.1": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.1.1.tgz#b54557b2e86214319405db819c4b6a370cf340c5" - integrity sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg== - dependencies: - "@noble/hashes" "~1.2.0" - "@scure/base" "~1.1.0" - -"@scure/bip39@1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.1.tgz#5cee8978656b272a917b7871c981e0541ad6ac2a" - integrity sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg== - dependencies: - "@noble/hashes" "~1.3.0" - "@scure/base" "~1.1.0" - -"@scure/bip39@1.2.2": - version "1.2.2" - resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.2.tgz#f3426813f4ced11a47489cbcf7294aa963966527" - integrity sha512-HYf9TUXG80beW+hGAt3TRM8wU6pQoYur9iNypTROm42dorCGmLnFe3eWjz3gOq6G62H2WRh0FCzAR1PI+29zIA== - dependencies: - "@noble/hashes" "~1.3.2" - "@scure/base" "~1.1.4" - "@sentry/core@5.30.0": version "5.30.0" resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.30.0.tgz#6b203664f69e75106ee8b5a2fe1d717379b331f3" @@ -2343,6 +1911,11 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== +"@solidity-parser/parser@^0.11.0": + version "0.11.1" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.11.1.tgz#fa840af64840c930f24a9c82c08d4a092a068add" + integrity sha512-H8BSBoKE8EubJa0ONqecA2TviT3TnHeC4NpgnAHSUiuhZoQBfPB4L2P9bs8R6AoTW10Endvh3vc+fomVMIDIYQ== + "@solidity-parser/parser@^0.12.0", "@solidity-parser/parser@^0.12.2": version "0.12.2" resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.12.2.tgz#1afad367cb29a2ed8cdd4a3a62701c2821fb578f" @@ -2355,10 +1928,12 @@ dependencies: antlr4ts "^0.5.0-alpha.4" -"@solidity-parser/parser@^0.18.0": - version "0.18.0" - resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.18.0.tgz#8e77a02a09ecce957255a2f48c9a7178ec191908" - integrity sha512-yfORGUIPgLck41qyN7nbwJRAx17/jAIXCTanHOJZhB6PJ1iAk/84b/xlsVKFSyNyLXIj0dhppoE0+CRws7wlzA== +"@solidity-parser/parser@^0.14.0": + version "0.14.1" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.14.1.tgz#179afb29f4e295a77cc141151f26b3848abc3c46" + integrity sha512-eLjj2L6AuQjBB6s/ibwCAc0DwrR5Ge+ys+wgWo+bviU7fV2nTMQhU63CGaDKXg9iTmMxwhkyoggdIR7ZGRfMgw== + dependencies: + antlr4ts "^0.5.0-alpha.4" "@solidity-parser/parser@^0.5.2": version "0.5.2" @@ -2576,6 +2151,11 @@ xhr "^2.2.0" xtend "^4.0.1" +"@types/abstract-leveldown@*": + version "5.0.2" + resolved "https://registry.yarnpkg.com/@types/abstract-leveldown/-/abstract-leveldown-5.0.2.tgz#ee81917fe38f770e29eec8139b6f16ee4a8b0a5f" + integrity sha512-+jA1XXF3jsz+Z7FcuiNqgK53hTa/luglT2TyTpKPqoYbxVY+mCPF22Rm+q3KPBrMHJwNXFrTViHszBOfU4vftQ== + "@types/bignumber.js@^5.0.0": version "5.0.0" resolved "https://registry.yarnpkg.com/@types/bignumber.js/-/bignumber.js-5.0.0.tgz#d9f1a378509f3010a3255e9cc822ad0eeb4ab969" @@ -2624,6 +2204,20 @@ "@types/minimatch" "*" "@types/node" "*" +"@types/level-errors@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/level-errors/-/level-errors-3.0.0.tgz#15c1f4915a5ef763b51651b15e90f6dc081b96a8" + integrity sha512-/lMtoq/Cf/2DVOm6zE6ORyOM+3ZVm/BvzEZVxUhf6bgh8ZHglXlBqxbxSlJeVp8FCbD3IVvk/VbsaNmDjrQvqQ== + +"@types/levelup@^4.3.0": + version "4.3.3" + resolved "https://registry.yarnpkg.com/@types/levelup/-/levelup-4.3.3.tgz#4dc2b77db079b1cf855562ad52321aa4241b8ef4" + integrity sha512-K+OTIjJcZHVlZQN1HmU64VtrC0jC3dXWQozuEIR9zVvltIk90zaGPM2AgT+fIkChpzHhFE3YnvFLCbLtzAmexA== + dependencies: + "@types/abstract-leveldown" "*" + "@types/level-errors" "*" + "@types/node" "*" + "@types/long@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9" @@ -2720,11 +2314,6 @@ abbrev@1.0.x: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" integrity sha1-kbR5JYinc4wl813W9jdSovh3YTU= -abitype@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.0.tgz#237176dace81d90d018bebf3a45cb42f2a2d9e97" - integrity sha512-NMeMah//6bJ56H5XRj8QCV4AwuW6hB6zqz2LnhhLdcWVQOsXki6/Pn3APeqxCma62nXIcmZWdu1DlHWS74umVQ== - abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" @@ -2753,6 +2342,17 @@ abstract-leveldown@^5.0.0, abstract-leveldown@~5.0.0: dependencies: xtend "~4.0.0" +abstract-leveldown@^6.2.1: + version "6.3.0" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.3.0.tgz#d25221d1e6612f820c35963ba4bd739928f6026a" + integrity sha512-TU5nlYgta8YrBMNpc9FwQzRbiXsj49gsALsXadbGHt9CROPzX5fB0rWDR5mtdpOOKa5XqRFpbj1QroPAoPzVjQ== + dependencies: + buffer "^5.5.0" + immediate "^3.2.3" + level-concat-iterator "~2.0.0" + level-supports "~1.0.0" + xtend "~4.0.0" + abstract-leveldown@~2.6.0: version "2.6.3" resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-2.6.3.tgz#1c5e8c6a5ef965ae8c35dfb3a8770c476b82c4b8" @@ -2760,6 +2360,17 @@ abstract-leveldown@~2.6.0: dependencies: xtend "~4.0.0" +abstract-leveldown@~6.2.1: + version "6.2.3" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz#036543d87e3710f2528e47040bc3261b77a9a8eb" + integrity sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ== + dependencies: + buffer "^5.5.0" + immediate "^3.2.3" + level-concat-iterator "~2.0.0" + level-supports "~1.0.0" + xtend "~4.0.0" + accepts@~1.3.7: version "1.3.7" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" @@ -2816,14 +2427,6 @@ agentkeepalive@^3.4.1: dependencies: humanize-ms "^1.2.1" -aggregate-error@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" - integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== - dependencies: - clean-stack "^2.0.0" - indent-string "^4.0.0" - ajv@^5.2.2: version "5.5.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" @@ -2844,34 +2447,17 @@ ajv@^6.10.0, ajv@^6.12.3: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.1: - version "8.12.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" - integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - amdefine@>=0.0.4: version "1.0.1" resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= -ansi-align@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" - integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== - dependencies: - string-width "^4.1.0" - ansi-colors@3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== -ansi-colors@4.1.1, ansi-colors@^4.1.0, ansi-colors@^4.1.1: +ansi-colors@^4.1.0, ansi-colors@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== @@ -2919,11 +2505,6 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -ansi-regex@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" - integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== - ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" @@ -2936,18 +2517,13 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" -ansi-styles@^4.0.0, ansi-styles@^4.1.0: +ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" -ansi-styles@^6.1.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" - integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== - antlr4ts@^0.5.0-alpha.4: version "0.5.0-alpha.4" resolved "https://registry.yarnpkg.com/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz#71702865a87478ed0b40c0709f422cf14d51652a" @@ -3021,11 +2597,6 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - arr-diff@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" @@ -3080,7 +2651,7 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -array-uniq@^1.0.1, array-uniq@^1.0.3: +array-uniq@1.0.3, array-uniq@^1.0.1, array-uniq@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= @@ -3137,17 +2708,12 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= -astral-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" - integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== - async-each@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== -async-eventemitter@^0.2.2: +async-eventemitter@^0.2.2, async-eventemitter@^0.2.4: version "0.2.4" resolved "https://registry.yarnpkg.com/async-eventemitter/-/async-eventemitter-0.2.4.tgz#f5e7c8ca7d3e46aab9ec40a292baf686a0bafaca" integrity sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw== @@ -3213,15 +2779,6 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== -axios@^1.6.7: - version "1.6.8" - resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66" - integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ== - dependencies: - follow-redirects "^1.15.6" - form-data "^4.0.0" - proxy-from-env "^1.1.0" - babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" @@ -3824,7 +3381,7 @@ big.js@^5.2.2: resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== -bignumber.js@*, bignumber.js@^9.0.0: +bignumber.js@*, bignumber.js@^9.0.0, bignumber.js@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5" integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA== @@ -3923,11 +3480,6 @@ bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2, bn.js@^5.1.3, bn.js@^5.2.0: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== -bn.js@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" - integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== - body-parser@1.19.0, body-parser@^1.16.0: version "1.19.0" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" @@ -3975,20 +3527,6 @@ borc@^3.0.0: json-text-sequence "~0.3.0" readable-stream "^3.6.0" -boxen@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" - integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ== - dependencies: - ansi-align "^3.0.0" - camelcase "^6.2.0" - chalk "^4.1.0" - cli-boxes "^2.2.1" - string-width "^4.2.2" - type-fest "^0.20.2" - widest-line "^3.1.0" - wrap-ansi "^7.0.0" - brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -3997,13 +3535,6 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -brace-expansion@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" - integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== - dependencies: - balanced-match "^1.0.0" - braces@^1.8.2: version "1.8.5" resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" @@ -4041,11 +3572,6 @@ brorand@^1.0.1, brorand@^1.1.0: resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= -brotli-wasm@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/brotli-wasm/-/brotli-wasm-2.0.1.tgz#2b3f4dc3db0c3e60d2635c055e6bac4ddf4bd3f5" - integrity sha512-+3USgYsC7bzb5yU0/p2HnnynZl0ak0E6uoIm4UW4Aby/8s8HFCq6NCfrrf1E9c3O8OCSzq3oYO1tUVqIi61Nww== - browser-readablestream-to-it@^1.0.1, browser-readablestream-to-it@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/browser-readablestream-to-it/-/browser-readablestream-to-it-1.0.3.tgz#ac3e406c7ee6cdf0a502dd55db33bab97f7fba76" @@ -4427,11 +3953,6 @@ camelcase@^5.0.0, camelcase@^5.3.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^6.0.0, camelcase@^6.2.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" - integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== - caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30001265: version "1.0.30001270" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001270.tgz#cc9c37a4ec5c1a8d616fc7bace902bb053b0cdea" @@ -4452,12 +3973,13 @@ caw@^2.0.1: tunnel-agent "^0.6.0" url-to-options "^1.0.1" -cbor@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/cbor/-/cbor-8.1.0.tgz#cfc56437e770b73417a2ecbfc9caf6b771af60d5" - integrity sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg== +cbor@^5.0.2: + version "5.2.0" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-5.2.0.tgz#4cca67783ccd6de7b50ab4ed62636712f287a67c" + integrity sha512-5IMhi9e1QU76ppa5/ajP1BmMWZ2FHkhAhjeVKQ/EFCgYSEaeVaoGtL7cxJskf9oCCk+XjzaIdc3IuU/dbA/o2A== dependencies: - nofilter "^3.1.0" + bignumber.js "^9.0.1" + nofilter "^1.0.4" chai@^4.2.0: version "4.3.4" @@ -4471,14 +3993,6 @@ chai@^4.2.0: pathval "^1.1.1" type-detect "^4.0.5" -chalk@4.1.2, chalk@^4.1.0, chalk@^4.1.1: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -4499,6 +4013,14 @@ chalk@^2.0.0, chalk@^2.3.1, chalk@^2.3.2, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +chalk@^4.1.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -4560,21 +4082,6 @@ chokidar@3.3.0: optionalDependencies: fsevents "~2.1.1" -chokidar@3.5.3: - version "3.5.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" - integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== - dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" - optionalDependencies: - fsevents "~2.3.2" - chokidar@^1.6.0: version "1.7.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" @@ -4660,16 +4167,6 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" -clean-stack@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" - integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== - -cli-boxes@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" - integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== - cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" @@ -4687,15 +4184,6 @@ cli-table3@^0.5.0: optionalDependencies: colors "^1.1.2" -cli-table3@^0.6.3: - version "0.6.4" - resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.4.tgz#d1c536b8a3f2e7bec58f67ac9e5769b1b30088b0" - integrity sha512-Lm3L0p+/npIQWNIiyF/nAn7T5dnOwR3xNTHXYEBFBFVPXzCVNZ5lqEC/1eo/EVfpDsQ1I+TX4ORPQgp+UI0CRw== - dependencies: - string-width "^4.2.0" - optionalDependencies: - "@colors/colors" "1.5.0" - cli-width@^2.0.0: version "2.2.1" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" @@ -4719,15 +4207,6 @@ cliui@^5.0.0: strip-ansi "^5.2.0" wrap-ansi "^5.1.0" -cliui@^7.0.2: - version "7.0.4" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" - integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^7.0.0" - clone-buffer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" @@ -4815,7 +4294,7 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -colors@^1.1.2: +colors@1.4.0, colors@^1.1.2: version "1.4.0" resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== @@ -5177,7 +4656,7 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0, cross-spawn@^7.0.3: +cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -5296,13 +4775,6 @@ debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: dependencies: ms "2.1.2" -debug@4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== - dependencies: - ms "2.1.2" - debug@^3.1.0: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" @@ -5328,11 +4800,6 @@ decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= -decamelize@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" - integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== - decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" @@ -5459,6 +4926,14 @@ deferred-leveldown@~4.0.0: abstract-leveldown "~5.0.0" inherits "^2.0.3" +deferred-leveldown@~5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz#27a997ad95408b61161aa69bd489b86c71b78058" + integrity sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw== + dependencies: + abstract-leveldown "~6.2.1" + inherits "^2.0.3" + define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" @@ -5579,11 +5054,6 @@ diff@3.5.0, diff@^3.5.0: resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== -diff@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" - integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== - diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" @@ -5593,13 +5063,6 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" -difflib@^0.2.4: - version "0.2.4" - resolved "https://registry.yarnpkg.com/difflib/-/difflib-0.2.4.tgz#b5e30361a6db023176d562892db85940a718f47e" - integrity sha512-9YVwmMb0wQHQNr5J9m6BSj6fk4pfGITGQOOs+D9Fl+INODWFOfvhIU1hNv6GgR1RBoC/9NJcwu77zShxV0kT7w== - dependencies: - heap ">= 0.2.0" - dir-glob@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" @@ -5726,11 +5189,6 @@ duplexify@^3.4.2, duplexify@^3.6.0: readable-stream "^2.0.0" stream-shift "^1.0.0" -eastasianwidth@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" - integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== - ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -5789,11 +5247,6 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -emoji-regex@^9.2.2: - version "9.2.2" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" - integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== - encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -5810,6 +5263,16 @@ encoding-down@5.0.4, encoding-down@~5.0.0: level-errors "^2.0.0" xtend "^4.0.1" +encoding-down@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/encoding-down/-/encoding-down-6.3.0.tgz#b1c4eb0e1728c146ecaef8e32963c549e76d082b" + integrity sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw== + dependencies: + abstract-leveldown "^6.2.1" + inherits "^2.0.3" + level-codec "^9.0.0" + level-errors "^2.0.0" + encoding@^0.1.11, encoding@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" @@ -5963,11 +5426,6 @@ escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1 resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= -escape-string-regexp@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - escodegen@1.8.x: version "1.8.1" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" @@ -6059,6 +5517,27 @@ eth-gas-reporter@^0.2.18: sha1 "^1.1.1" sync-request "^6.0.0" +eth-gas-reporter@^0.2.24: + version "0.2.24" + resolved "https://registry.yarnpkg.com/eth-gas-reporter/-/eth-gas-reporter-0.2.24.tgz#768721fec7de02b566e4ebfd123466d275d7035c" + integrity sha512-RbXLC2bnuPHzIMU/rnLXXlb6oiHEEKu7rq2UrAX/0mfo0Lzrr/kb9QTjWjfz8eNvc+uu6J8AuBwI++b+MLNI2w== + dependencies: + "@ethersproject/abi" "^5.0.0-beta.146" + "@solidity-parser/parser" "^0.14.0" + cli-table3 "^0.5.0" + colors "1.4.0" + ethereumjs-util "6.2.0" + ethers "^4.0.40" + fs-readdir-recursive "^1.1.0" + lodash "^4.17.14" + markdown-table "^1.1.3" + mocha "^7.1.1" + req-cwd "^2.0.0" + request "^2.88.0" + request-promise-native "^1.0.5" + sha1 "^1.1.1" + sync-request "^6.0.0" + eth-json-rpc-errors@^1.0.1: version "1.1.1" resolved "https://registry.yarnpkg.com/eth-json-rpc-errors/-/eth-json-rpc-errors-1.1.1.tgz#148377ef55155585981c21ff574a8937f9d6991f" @@ -6230,7 +5709,7 @@ ethereum-common@^0.0.18: resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.0.18.tgz#2fdc3576f232903358976eb39da783213ff9523f" integrity sha1-L9w1dvIykDNYl26znaeDIT/5Uj8= -ethereum-cryptography@0.1.3, ethereum-cryptography@^0.1.2, ethereum-cryptography@^0.1.3: +ethereum-cryptography@^0.1.2, ethereum-cryptography@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" integrity sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ== @@ -6251,26 +5730,6 @@ ethereum-cryptography@0.1.3, ethereum-cryptography@^0.1.2, ethereum-cryptography secp256k1 "^4.0.1" setimmediate "^1.0.5" -ethereum-cryptography@^1.0.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz#5ccfa183e85fdaf9f9b299a79430c044268c9b3a" - integrity sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw== - dependencies: - "@noble/hashes" "1.2.0" - "@noble/secp256k1" "1.7.1" - "@scure/bip32" "1.1.5" - "@scure/bip39" "1.1.1" - -ethereum-cryptography@^2.0.0, ethereum-cryptography@^2.1.2, ethereum-cryptography@^2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-2.1.3.tgz#1352270ed3b339fe25af5ceeadcf1b9c8e30768a" - integrity sha512-BlwbIL7/P45W8FGW2r7LGuvoEZ+7PWsniMvQ4p5s2xCyw9tmaDlpfsN9HjAucbF+t/qpVHwZUisgfK24TCW8aA== - dependencies: - "@noble/curves" "1.3.0" - "@noble/hashes" "1.3.3" - "@scure/bip32" "1.3.3" - "@scure/bip39" "1.2.2" - ethereum-ens@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/ethereum-ens/-/ethereum-ens-0.8.0.tgz#6d0f79acaa61fdbc87d2821779c4e550243d4c57" @@ -6406,7 +5865,7 @@ ethereumjs-util@6.2.0: rlp "^2.2.3" secp256k1 "^3.0.1" -ethereumjs-util@6.2.1, ethereumjs-util@^6.0.0, ethereumjs-util@^6.1.0, ethereumjs-util@^6.2.0, ethereumjs-util@^6.2.1: +ethereumjs-util@6.2.1, ethereumjs-util@^6.0.0, ethereumjs-util@^6.1.0, ethereumjs-util@^6.2.0: version "6.2.1" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69" integrity sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw== @@ -6607,7 +6066,7 @@ ethjs-unit@0.1.6: bn.js "4.11.6" number-to-bn "1.7.0" -ethjs-util@0.1.6, ethjs-util@^0.1.3, ethjs-util@^0.1.6: +ethjs-util@0.1.6, ethjs-util@^0.1.3: version "0.1.6" resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.6.tgz#f308b62f185f9fe6237132fb2a9818866a5cd536" integrity sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w== @@ -7029,14 +6488,6 @@ find-up@3.0.0, find-up@^3.0.0: dependencies: locate-path "^3.0.0" -find-up@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== - dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" - find-up@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" @@ -7082,11 +6533,6 @@ flat@^4.1.0: dependencies: is-buffer "~2.0.3" -flat@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" - integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== - flow-stoplight@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/flow-stoplight/-/flow-stoplight-1.0.0.tgz#4a292c5bcff8b39fa6cc0cb1a853d86f27eeff7b" @@ -7105,11 +6551,6 @@ follow-redirects@^1.12.1: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.4.tgz#838fdf48a8bbdd79e52ee51fb1c94e3ed98b9379" integrity sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g== -follow-redirects@^1.15.6: - version "1.15.6" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.6.tgz#7f815c0cda4249c74ff09e95ef97c23b5fd0399b" - integrity sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA== - for-each@^0.3.3, for-each@~0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -7134,14 +6575,6 @@ foreach@^2.0.5: resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= -foreground-child@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" - integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg== - dependencies: - cross-spawn "^7.0.0" - signal-exit "^4.0.1" - forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -7385,7 +6818,7 @@ get-caller-file@^1.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== -get-caller-file@^2.0.1, get-caller-file@^2.0.5: +get-caller-file@^2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -7626,48 +7059,26 @@ glob@7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" -glob@7.2.0, glob@^7.0.0, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4: - version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== +glob@^5.0.15: + version "5.0.15" + resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" + integrity sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E= dependencies: - fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "^3.0.4" + minimatch "2 || 3" once "^1.3.0" path-is-absolute "^1.0.0" -glob@8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" - integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== +glob@^7.0.0, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "^5.0.1" - once "^1.3.0" - -glob@^10.3.10: - version "10.3.12" - resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.12.tgz#3a65c363c2e9998d220338e88a5f6ac97302960b" - integrity sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg== - dependencies: - foreground-child "^3.1.0" - jackspeak "^2.3.6" - minimatch "^9.0.1" - minipass "^7.0.4" - path-scurry "^1.10.2" - -glob@^5.0.15: - version "5.0.15" - resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" - integrity sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E= - dependencies: - inflight "^1.0.4" - inherits "2" - minimatch "2 || 3" + minimatch "^3.0.4" once "^1.3.0" path-is-absolute "^1.0.0" @@ -7865,74 +7276,66 @@ hardhat-abi-exporter@^2.8.0: "@ethersproject/abi" "^5.5.0" delete-empty "^3.0.0" -hardhat-gas-reporter@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/hardhat-gas-reporter/-/hardhat-gas-reporter-2.1.1.tgz#fc91d437ee841ff32df1340025b0198201df92da" - integrity sha512-h3PAU7iDiPNqPA4i7GcKgnIZsN3UIdALM4gdJNoTtfmF9PGVne5nFZRNX1N7Y2Mx+znbBqeV1mS7o/i6T7NkTg== - dependencies: - "@ethersproject/abi" "^5.7.0" - "@ethersproject/bytes" "^5.7.0" - "@ethersproject/units" "^5.7.0" - "@solidity-parser/parser" "^0.18.0" - axios "^1.6.7" - brotli-wasm "^2.0.1" - chalk "4.1.2" - cli-table3 "^0.6.3" - ethereum-cryptography "^2.1.3" - glob "^10.3.10" - jsonschema "^1.4.1" - lodash "^4.17.21" - markdown-table "2.0.0" +hardhat-gas-reporter@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/hardhat-gas-reporter/-/hardhat-gas-reporter-1.0.8.tgz#93ce271358cd748d9c4185dbb9d1d5525ec145e0" + integrity sha512-1G5thPnnhcwLHsFnl759f2tgElvuwdkzxlI65fC9PwxYMEe9cmjkVAAWTf3/3y8uP6ZSPiUiOW8PgZnykmZe0g== + dependencies: + array-uniq "1.0.3" + eth-gas-reporter "^0.2.24" sha1 "^1.1.1" - viem "2.7.14" -hardhat@^2.22.2: - version "2.22.3" - resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.22.3.tgz#50605daca6b29862397e446c42ec14c89430bec3" - integrity sha512-k8JV2ECWNchD6ahkg2BR5wKVxY0OiKot7fuxiIpRK0frRqyOljcR2vKwgWSLw6YIeDcNNA4xybj7Og7NSxr2hA== +hardhat@^2.6.5: + version "2.6.6" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.6.6.tgz#11d2dc4c1659fcbb8632de9399b1b4999f8b3628" + integrity sha512-GneZAwvNxf3cNobYTmMSp2qGX/yJyUmHD/xjW+8zF2AgWkLS33CHmGn4aRFKqfjsk7BS7FU6jqMmmZhHHO3gUw== dependencies: + "@ethereumjs/block" "^3.4.0" + "@ethereumjs/blockchain" "^5.4.0" + "@ethereumjs/common" "^2.4.0" + "@ethereumjs/tx" "^3.3.0" + "@ethereumjs/vm" "^5.5.2" "@ethersproject/abi" "^5.1.2" - "@metamask/eth-sig-util" "^4.0.0" - "@nomicfoundation/edr" "^0.3.5" - "@nomicfoundation/ethereumjs-common" "4.0.4" - "@nomicfoundation/ethereumjs-tx" "5.0.4" - "@nomicfoundation/ethereumjs-util" "9.0.4" - "@nomicfoundation/solidity-analyzer" "^0.1.0" "@sentry/node" "^5.18.1" + "@solidity-parser/parser" "^0.11.0" "@types/bn.js" "^5.1.0" "@types/lru-cache" "^5.1.0" + abort-controller "^3.0.0" adm-zip "^0.4.16" - aggregate-error "^3.0.0" ansi-escapes "^4.3.0" - boxen "^5.1.2" chalk "^2.4.2" chokidar "^3.4.0" ci-info "^2.0.0" debug "^4.1.1" enquirer "^2.3.0" env-paths "^2.2.0" - ethereum-cryptography "^1.0.3" + eth-sig-util "^2.5.2" + ethereum-cryptography "^0.1.2" ethereumjs-abi "^0.6.8" + ethereumjs-util "^7.1.0" find-up "^2.1.0" fp-ts "1.19.3" fs-extra "^7.0.1" - glob "7.2.0" + glob "^7.1.3" + https-proxy-agent "^5.0.0" immutable "^4.0.0-rc.12" io-ts "1.10.4" - keccak "^3.0.2" lodash "^4.17.11" + merkle-patricia-tree "^4.2.0" mnemonist "^0.38.0" - mocha "^10.0.0" - p-map "^4.0.0" + mocha "^7.1.2" + node-fetch "^2.6.0" + qs "^6.7.0" raw-body "^2.4.1" resolve "1.17.0" semver "^6.3.0" + slash "^3.0.0" solc "0.7.3" source-map-support "^0.5.13" stacktrace-parser "^0.1.10" + "true-case-path" "^2.2.1" tsort "0.0.1" - undici "^5.14.0" - uuid "^8.3.2" + uuid "^3.3.2" ws "^7.4.6" has-ansi@^2.0.0: @@ -8074,11 +7477,6 @@ heap@0.2.6: resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.6.tgz#087e1f10b046932fc8594dd9e6d378afc9d1e5ac" integrity sha1-CH4fELBGky/IWU3Z5tN4r8nR5aw= -"heap@>= 0.2.0": - version "0.2.7" - resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.7.tgz#1e6adf711d3f27ce35a81fe3b7bd576c2260a8fc" - integrity sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg== - highlight.js@^10.4.1: version "10.7.3" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" @@ -9081,11 +8479,6 @@ is-unc-path@^1.0.0: dependencies: unc-path-regex "^0.1.2" -is-unicode-supported@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" - integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== - is-utf8@^0.2.0, is-utf8@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" @@ -9165,11 +8558,6 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= -isows@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.3.tgz#93c1cf0575daf56e7120bab5c8c448b0809d0d74" - integrity sha512-2cKei4vlmg2cxEjm3wVSqn8pcoRF/LX/wpifuuNquFO4SQmPwarClT+SUCA2lt+l581tTeZIPIZuIDo2jWN1fg== - isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -9264,15 +8652,6 @@ it-to-stream@^1.0.0: p-fifo "^1.0.0" readable-stream "^3.6.0" -jackspeak@^2.3.6: - version "2.3.6" - resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.3.6.tgz#647ecc472238aee4b06ac0e461acc21a8c505ca8" - integrity sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ== - dependencies: - "@isaacs/cliui" "^8.0.2" - optionalDependencies: - "@pkgjs/parseargs" "^0.11.0" - js-sha3@0.5.7, js-sha3@^0.5.7: version "0.5.7" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" @@ -9314,13 +8693,6 @@ js-yaml@3.x, js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -9398,11 +8770,6 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== -json-schema-traverse@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" - integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== - json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" @@ -9473,11 +8840,6 @@ jsonschema@^1.2.4: resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.4.0.tgz#1afa34c4bc22190d8e42271ec17ac8b3404f87b2" integrity sha512-/YgW6pRMr6M7C+4o8kS+B/2myEpHCrxO4PEWnqJNBFMjn7EWXqlQ4tGwL6xTHeRplwuZmcAncdvfOad1nT2yMw== -jsonschema@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.4.1.tgz#cc4c3f0077fb4542982973d8a083b6b34f482dab" - integrity sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ== - jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -9515,15 +8877,6 @@ keccak@^3.0.0: node-gyp-build "^4.2.0" readable-stream "^3.6.0" -keccak@^3.0.2: - version "3.0.4" - resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.4.tgz#edc09b89e633c0549da444432ecf062ffadee86d" - integrity sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q== - dependencies: - node-addon-api "^2.0.0" - node-gyp-build "^4.2.0" - readable-stream "^3.6.0" - keyv@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.0.0.tgz#44923ba39e68b12a7cec7df6c3268c031f2ef373" @@ -9654,6 +9007,11 @@ level-codec@~7.0.0: resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-7.0.1.tgz#341f22f907ce0f16763f24bddd681e395a0fb8a7" integrity sha512-Ua/R9B9r3RasXdRmOtd+t9TCOEIIlts+TN/7XTT2unhDaL6sJn83S3rUyljbr6lVtw49N3/yA0HHjpV6Kzb2aQ== +level-concat-iterator@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz#1d1009cf108340252cb38c51f9727311193e6263" + integrity sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw== + level-errors@^1.0.3: version "1.1.2" resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-1.1.2.tgz#4399c2f3d3ab87d0625f7e3676e2d807deff404d" @@ -9703,6 +9061,15 @@ level-iterator-stream@~3.0.0: readable-stream "^2.3.6" xtend "^4.0.0" +level-iterator-stream@~4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-4.0.2.tgz#7ceba69b713b0d7e22fcc0d1f128ccdc8a24f79c" + integrity sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q== + dependencies: + inherits "^2.0.4" + readable-stream "^3.4.0" + xtend "^4.0.2" + level-mem@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/level-mem/-/level-mem-3.0.1.tgz#7ce8cf256eac40f716eb6489654726247f5a89e5" @@ -9711,6 +9078,22 @@ level-mem@^3.0.1: level-packager "~4.0.0" memdown "~3.0.0" +level-mem@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/level-mem/-/level-mem-5.0.1.tgz#c345126b74f5b8aa376dc77d36813a177ef8251d" + integrity sha512-qd+qUJHXsGSFoHTziptAKXoLX87QjR7v2KMbqncDXPxQuCdsQlzmyX+gwrEHhlzn08vkf8TyipYyMmiC6Gobzg== + dependencies: + level-packager "^5.0.3" + memdown "^5.0.0" + +level-packager@^5.0.3: + version "5.1.1" + resolved "https://registry.yarnpkg.com/level-packager/-/level-packager-5.1.1.tgz#323ec842d6babe7336f70299c14df2e329c18939" + integrity sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ== + dependencies: + encoding-down "^6.3.0" + levelup "^4.3.2" + level-packager@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/level-packager/-/level-packager-4.0.1.tgz#7e7d3016af005be0869bc5fa8de93d2a7f56ffe6" @@ -9742,6 +9125,13 @@ level-sublevel@6.6.4: typewiselite "~1.0.0" xtend "~4.0.0" +level-supports@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-1.0.1.tgz#2f530a596834c7301622521988e2c36bb77d122d" + integrity sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg== + dependencies: + xtend "^4.0.2" + level-ws@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-0.0.0.tgz#372e512177924a00424b0b43aef2bb42496d228b" @@ -9759,6 +9149,15 @@ level-ws@^1.0.0: readable-stream "^2.2.8" xtend "^4.0.1" +level-ws@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-2.0.0.tgz#207a07bcd0164a0ec5d62c304b4615c54436d339" + integrity sha512-1iv7VXx0G9ec1isqQZ7y5LmoZo/ewAsyDHNA8EFDW5hqH2Kqovm33nSFkSdnLLAK+I5FlT+lo5Cw9itGe+CpQA== + dependencies: + inherits "^2.0.3" + readable-stream "^3.1.0" + xtend "^4.0.1" + levelup@3.1.1, levelup@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/levelup/-/levelup-3.1.1.tgz#c2c0b3be2b4dc316647c53b42e2f559e232d2189" @@ -9782,6 +9181,17 @@ levelup@^1.2.1: semver "~5.4.1" xtend "~4.0.0" +levelup@^4.3.2: + version "4.4.0" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-4.4.0.tgz#f89da3a228c38deb49c48f88a70fb71f01cafed6" + integrity sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ== + dependencies: + deferred-leveldown "~5.3.0" + level-errors "~2.0.0" + level-iterator-stream "~4.0.0" + level-supports "~1.0.0" + xtend "~4.0.0" + levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" @@ -9850,13 +9260,6 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" -locate-path@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== - dependencies: - p-locate "^5.0.0" - lodash._reinterpolate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" @@ -9932,11 +9335,6 @@ lodash.templatesettings@^4.0.0: dependencies: lodash._reinterpolate "^3.0.0" -lodash.truncate@^4.4.2: - version "4.4.2" - resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" - integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== - lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" @@ -9964,14 +9362,6 @@ log-symbols@3.0.0: dependencies: chalk "^2.4.2" -log-symbols@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" - integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== - dependencies: - chalk "^4.1.0" - is-unicode-supported "^0.1.0" - long@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" @@ -10024,11 +9414,6 @@ lru-cache@5.1.1, lru-cache@^5.1.1: dependencies: yallist "^3.0.2" -lru-cache@^10.2.0: - version "10.2.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.2.0.tgz#0bd445ca57363465900f4d1f9bd8db343a4d95c3" - integrity sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q== - lru-cache@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-3.2.0.tgz#71789b3b7f5399bec8565dda38aa30d2a097efee" @@ -10137,13 +9522,6 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" -markdown-table@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-2.0.0.tgz#194a90ced26d31fe753d8b9434430214c011865b" - integrity sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A== - dependencies: - repeat-string "^1.0.0" - markdown-table@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-1.1.3.tgz#9fcb69bcfdb8717bfd0398c6ec2d93036ef8de60" @@ -10154,6 +9532,11 @@ math-random@^1.0.1: resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c" integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A== +mcl-wasm@^0.7.1: + version "0.7.9" + resolved "https://registry.yarnpkg.com/mcl-wasm/-/mcl-wasm-0.7.9.tgz#c1588ce90042a8700c3b60e40efb339fc07ab87f" + integrity sha512-iJIUcQWA88IJB/5L15GnJVnSQJmf/YaxxV6zRavv83HILHaJQb6y0iFyDMdDO0gN8X37tdxmAOrH/P8B6RB8sQ== + md5.js@^1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" @@ -10196,6 +9579,18 @@ memdown@^1.0.0: ltgt "~2.2.0" safe-buffer "~5.1.1" +memdown@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/memdown/-/memdown-5.1.0.tgz#608e91a9f10f37f5b5fe767667a8674129a833cb" + integrity sha512-B3J+UizMRAlEArDjWHTMmadet+UKwHd3UjMgGBkZcKAxAYVPS9o0Yeiha4qvz7iGiL2Sb3igUft6p7nbFWctpw== + dependencies: + abstract-leveldown "~6.2.1" + functional-red-black-tree "~1.0.1" + immediate "~3.2.3" + inherits "~2.0.1" + ltgt "~2.2.0" + safe-buffer "~5.2.0" + memdown@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/memdown/-/memdown-3.0.0.tgz#93aca055d743b20efc37492e9e399784f2958309" @@ -10310,16 +9705,24 @@ merkle-patricia-tree@^2.1.2, merkle-patricia-tree@^2.3.2: rlp "^2.0.0" semaphore ">=1.0.1" +merkle-patricia-tree@^4.2.0, merkle-patricia-tree@^4.2.1: + version "4.2.2" + resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-4.2.2.tgz#6dec17855370172458244c2f42c989dd60b773a3" + integrity sha512-eqZYNTshcYx9aESkSPr71EqwsR/QmpnObDEV4iLxkt/x/IoLYZYjJvKY72voP/27Vy61iMOrfOG6jrn7ttXD+Q== + dependencies: + "@types/levelup" "^4.3.0" + ethereumjs-util "^7.1.2" + level-mem "^5.0.1" + level-ws "^2.0.0" + readable-stream "^3.6.0" + rlp "^2.2.4" + semaphore-async-await "^1.5.1" + methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= -micro-ftch@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/micro-ftch/-/micro-ftch-0.3.1.tgz#6cb83388de4c1f279a034fb0cf96dfc050853c5f" - integrity sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg== - micromatch@^2.1.5: version "2.3.11" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" @@ -10443,27 +9846,6 @@ minimalistic-crypto-utils@^1.0.1: dependencies: brace-expansion "^1.1.7" -minimatch@5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" - integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== - dependencies: - brace-expansion "^2.0.1" - -minimatch@^5.0.1: - version "5.1.6" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" - integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== - dependencies: - brace-expansion "^2.0.1" - -minimatch@^9.0.1: - version "9.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.4.tgz#8e49c731d1749cbec05050ee5145147b32496a51" - integrity sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw== - dependencies: - brace-expansion "^2.0.1" - minimist-options@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" @@ -10499,11 +9881,6 @@ minipass@^2.3.5, minipass@^2.6.0, minipass@^2.9.0: safe-buffer "^5.1.2" yallist "^3.0.0" -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.4: - version "7.0.4" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c" - integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== - minizlib@^1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" @@ -10568,32 +9945,6 @@ mnemonist@^0.38.0: dependencies: obliterator "^1.6.1" -mocha@^10.0.0, mocha@^10.2.0: - version "10.4.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.4.0.tgz#ed03db96ee9cfc6d20c56f8e2af07b961dbae261" - integrity sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA== - dependencies: - ansi-colors "4.1.1" - browser-stdout "1.3.1" - chokidar "3.5.3" - debug "4.3.4" - diff "5.0.0" - escape-string-regexp "4.0.0" - find-up "5.0.0" - glob "8.1.0" - he "1.2.0" - js-yaml "4.1.0" - log-symbols "4.1.0" - minimatch "5.0.1" - ms "2.1.3" - serialize-javascript "6.0.0" - strip-json-comments "3.1.1" - supports-color "8.1.1" - workerpool "6.2.1" - yargs "16.2.0" - yargs-parser "20.2.4" - yargs-unparser "2.0.0" - mocha@^4.0.1: version "4.1.0" resolved "https://registry.yarnpkg.com/mocha/-/mocha-4.1.0.tgz#7d86cfbcf35cb829e2754c32e17355ec05338794" @@ -10677,7 +10028,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@2.1.3, ms@^2.0.0, ms@^2.1.1: +ms@^2.0.0, ms@^2.1.1: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -10959,10 +10310,10 @@ node-releases@^2.0.0: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== -nofilter@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-3.1.0.tgz#c757ba68801d41ff930ba2ec55bab52ca184aa66" - integrity sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g== +nofilter@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-1.0.4.tgz#78d6f4b6a613e7ced8b015cec534625f7667006e" + integrity sha512-N8lidFp+fCz+TD51+haYdbDGrcBWwuHX40F5+z0qkUjMJ5Tp+rdSuAkMJ9N9eoolDlEVTf6u5icM+cNKkKW2mA== nopt@3.x: version "3.0.6" @@ -11428,13 +10779,6 @@ p-limit@^2.0.0, p-limit@^2.2.0: dependencies: p-try "^2.0.0" -p-limit@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" @@ -11456,13 +10800,6 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" -p-locate@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== - dependencies: - p-limit "^3.0.2" - p-map-series@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-1.0.0.tgz#bf98fe575705658a9e1351befb85ae4c1f07bdca" @@ -11475,13 +10812,6 @@ p-map@^2.1.0: resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== -p-map@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" - integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== - dependencies: - aggregate-error "^3.0.0" - p-pipe@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/p-pipe/-/p-pipe-1.2.0.tgz#4b1a11399a11520a67790ee5a0c1d5881d6befe9" @@ -11736,14 +11066,6 @@ path-parse@^1.0.6: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-scurry@^1.10.2: - version "1.10.2" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.2.tgz#8f6357eb1239d5fa1da8b9f70e9c080675458ba7" - integrity sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA== - dependencies: - lru-cache "^10.2.0" - minipass "^5.0.0 || ^6.0.2 || ^7.0.0" - path-starts-with@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/path-starts-with/-/path-starts-with-2.0.0.tgz#ffd6d51926cd497022b44d392196033d5451892f" @@ -11984,11 +11306,6 @@ proxy-addr@~2.0.5: forwarded "0.2.0" ipaddr.js "1.9.1" -proxy-from-env@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" - integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== - prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" @@ -12310,7 +11627,7 @@ read@1, read@~1.0.1: string_decoder "~1.1.1" util-deprecate "~1.0.1" -"readable-stream@2 || 3", readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.0.6, readable-stream@^3.4.0, readable-stream@^3.6.0: +"readable-stream@2 || 3", readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.0.6, readable-stream@^3.1.0, readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -12512,7 +11829,7 @@ repeat-element@^1.1.2: resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== -repeat-string@^1.0.0, repeat-string@^1.5.2, repeat-string@^1.6.1: +repeat-string@^1.5.2, repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= @@ -12590,7 +11907,7 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= -require-from-string@^2.0.0, require-from-string@^2.0.2: +require-from-string@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== @@ -12855,6 +12172,11 @@ seek-bzip@^1.0.5: dependencies: commander "^2.8.1" +semaphore-async-await@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/semaphore-async-await/-/semaphore-async-await-1.5.1.tgz#857bef5e3644601ca4b9570b87e9df5ca12974fa" + integrity sha1-hXvvXjZEYBykuVcLh+nfXKEpdPo= + semaphore@>=1.0.1, semaphore@^1.0.3, semaphore@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/semaphore/-/semaphore-1.1.0.tgz#aaad8b86b20fe8e9b32b16dc2ee682a8cd26a8aa" @@ -12906,13 +12228,6 @@ send@0.17.1: range-parser "~1.2.1" statuses "~1.5.0" -serialize-javascript@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" - integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== - dependencies: - randombytes "^2.1.0" - serve-static@1.14.1: version "1.14.1" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" @@ -13039,11 +12354,6 @@ signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.5.tgz#9e3e8cc0c75a99472b44321033a7702e7738252f" integrity sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ== -signal-exit@^4.0.1: - version "4.1.0" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" - integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== - simple-concat@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" @@ -13073,15 +12383,6 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -slice-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" - integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== - dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" - slide@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" @@ -13202,31 +12503,6 @@ solidity-coverage@^0.7.9: shelljs "^0.8.3" web3-utils "^1.3.0" -solidity-coverage@^0.8.0: - version "0.8.12" - resolved "https://registry.yarnpkg.com/solidity-coverage/-/solidity-coverage-0.8.12.tgz#c4fa2f64eff8ada7a1387b235d6b5b0e6c6985ed" - integrity sha512-8cOB1PtjnjFRqOgwFiD8DaUsYJtVJ6+YdXQtSZDrLGf8cdhhh8xzTtGzVTGeBf15kTv0v7lYPJlV/az7zLEPJw== - dependencies: - "@ethersproject/abi" "^5.0.9" - "@solidity-parser/parser" "^0.18.0" - chalk "^2.4.2" - death "^1.1.0" - difflib "^0.2.4" - fs-extra "^8.1.0" - ghost-testrpc "^0.0.2" - global-modules "^2.0.0" - globby "^10.0.1" - jsonschema "^1.2.4" - lodash "^4.17.21" - mocha "^10.2.0" - node-emoji "^1.10.0" - pify "^4.0.1" - recursive-readdir "^2.2.2" - sc-istanbul "^0.4.5" - semver "^7.3.4" - shelljs "^0.8.3" - web3-utils "^1.3.6" - solium-plugin-security@0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/solium-plugin-security/-/solium-plugin-security-0.1.1.tgz#2a87bcf8f8c3abf7d198e292e4ac080284e3f3f6" @@ -13491,15 +12767,6 @@ strict-uri-encode@^2.0.0: resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY= -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -13517,6 +12784,15 @@ string-width@^1.0.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" +"string-width@^1.0.2 || 2 || 3 || 4": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^3.0.0, string-width@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" @@ -13526,15 +12802,6 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" -string-width@^5.0.1, string-width@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" - integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== - dependencies: - eastasianwidth "^0.2.0" - emoji-regex "^9.2.2" - strip-ansi "^7.0.1" - string.prototype.trim@~1.2.4: version "1.2.5" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.5.tgz#a587bcc8bfad8cb9829a577f5de30dd170c1682c" @@ -13579,13 +12846,6 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" @@ -13607,12 +12867,12 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^7.0.1: - version "7.1.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" - integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== +strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: - ansi-regex "^6.0.1" + ansi-regex "^5.0.1" strip-bom@^2.0.0: version "2.0.0" @@ -13674,11 +12934,6 @@ strip-json-comments@2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -strip-json-comments@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - strip-outer@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/strip-outer/-/strip-outer-1.0.1.tgz#b2fd2abf6604b9d1e6013057195df836b8a9d631" @@ -13714,13 +12969,6 @@ supports-color@6.0.0: dependencies: has-flag "^3.0.0" -supports-color@8.1.1: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" - supports-color@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" @@ -13780,17 +13028,6 @@ sync-rpc@^1.2.1: dependencies: get-port "^3.1.0" -table@^6.8.0: - version "6.8.2" - resolved "https://registry.yarnpkg.com/table/-/table-6.8.2.tgz#c5504ccf201213fa227248bdc8c5569716ac6c58" - integrity sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA== - dependencies: - ajv "^8.0.1" - lodash.truncate "^4.4.2" - slice-ansi "^4.0.0" - string-width "^4.2.3" - strip-ansi "^6.0.1" - tape@^4.6.3: version "4.14.0" resolved "https://registry.yarnpkg.com/tape/-/tape-4.14.0.tgz#e4d46097e129817175b90925f2385f6b1bcfa826" @@ -14078,6 +13315,11 @@ trim-right@^1.0.1: resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= +"true-case-path@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-2.2.1.tgz#c5bf04a5bbec3fd118be4084461b3a27c4d796bf" + integrity sha512-0z3j8R7MCjy10kc/g+qg7Ln3alJTodw9aDuVWZa3uiWqfuBMKeAeP2ocWcxoyM3D73yz3Jt/Pu4qPr4wHSdB/Q== + truffle-flattener@^1.2.9: version "1.5.0" resolved "https://registry.yarnpkg.com/truffle-flattener/-/truffle-flattener-1.5.0.tgz#c358fa3e5cb0a663429f7912a58c4f0879414e64" @@ -14121,7 +13363,7 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" -tweetnacl-util@^0.15.0, tweetnacl-util@^0.15.1: +tweetnacl-util@^0.15.0: version "0.15.1" resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b" integrity sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw== @@ -14153,11 +13395,6 @@ type-fest@^0.18.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== -type-fest@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" - integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== - type-fest@^0.21.3: version "0.21.3" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" @@ -14302,13 +13539,6 @@ underscore@^1.8.3: resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.1.tgz#0c1c6bd2df54b6b69f2314066d65b6cde6fcf9d1" integrity sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g== -undici@^5.14.0: - version "5.28.3" - resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.3.tgz#a731e0eff2c3fcfd41c1169a869062be222d1e5b" - integrity sha512-3ItfzbrhDlINjaP0duwnNsKpDQk3acHI3gVJ1z4fmwMK31k5G9OVIAMLSIaP6w4FaGkaAkN6zaQO9LUvZ1t7VA== - dependencies: - "@fastify/busboy" "^2.0.0" - union-value@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" @@ -14446,7 +13676,7 @@ util-promisify@^2.1.0: dependencies: object.getownpropertydescriptors "^2.0.3" -util.promisify@^1.0.0: +util.promisify@^1.0.0, util.promisify@^1.0.1: version "1.1.1" resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.1.1.tgz#77832f57ced2c9478174149cae9b96e9918cd54b" integrity sha512-/s3UsZUrIfa6xDhr7zZhnE9SLQ5RIXyYfiVnMMyMDzOc8WhWN4Nbh36H842OyurKbCDAesZOJaVyvmSl6fhGQw== @@ -14538,20 +13768,6 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" -viem@2.7.14: - version "2.7.14" - resolved "https://registry.yarnpkg.com/viem/-/viem-2.7.14.tgz#347d316cb5400f0b896b2205b1bc8073aa5e27e0" - integrity sha512-5b1KB1gXli02GOQHZIUsRluNUwssl2t4hqdFAzyWPwJ744N83jAOBOjOkrGz7K3qMIv9b0GQt3DoZIErSQTPkQ== - dependencies: - "@adraffy/ens-normalize" "1.10.0" - "@noble/curves" "1.2.0" - "@noble/hashes" "1.3.2" - "@scure/bip32" "1.3.2" - "@scure/bip39" "1.2.1" - abitype "1.0.0" - isows "1.0.3" - ws "8.13.0" - vinyl-fs@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-3.0.3.tgz#c85849405f67428feabbbd5c5dbdd64f47d31bc7" @@ -15770,20 +14986,6 @@ web3-utils@1.7.1: randombytes "^2.1.0" utf8 "3.0.0" -web3-utils@^1.3.6: - version "1.10.4" - resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.10.4.tgz#0daee7d6841641655d8b3726baf33b08eda1cbec" - integrity sha512-tsu8FiKJLk2PzhDl9fXbGUWTkkVXYhtTA+SmEFkKft+9BgwLxfCRpU96sWv7ICC8zixBNd3JURVoiR3dUXgP8A== - dependencies: - "@ethereumjs/util" "^8.1.0" - bn.js "^5.2.1" - ethereum-bloom-filters "^1.0.6" - ethereum-cryptography "^2.1.2" - ethjs-unit "0.1.6" - number-to-bn "1.7.0" - randombytes "^2.1.0" - utf8 "3.0.0" - web3@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3/-/web3-1.2.11.tgz#50f458b2e8b11aa37302071c170ed61cff332975" @@ -15961,13 +15163,6 @@ wide-align@^1.1.0: dependencies: string-width "^1.0.2 || 2 || 3 || 4" -widest-line@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" - integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== - dependencies: - string-width "^4.0.0" - windows-release@^3.1.0: version "3.3.3" resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-3.3.3.tgz#1c10027c7225743eec6b89df160d64c2e0293999" @@ -15985,20 +15180,6 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= -workerpool@6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" - integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== - -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" @@ -16016,15 +15197,6 @@ wrap-ansi@^5.1.0: string-width "^3.0.0" strip-ansi "^5.0.0" -wrap-ansi@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" - integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== - dependencies: - ansi-styles "^6.1.0" - string-width "^5.0.1" - strip-ansi "^7.0.1" - wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -16076,11 +15248,6 @@ ws@7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== -ws@8.13.0: - version "8.13.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" - integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== - ws@^3.0.0: version "3.3.3" resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" @@ -16144,7 +15311,7 @@ xmlhttprequest@1.8.0: resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" integrity sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw= -xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0, xtend@~4.0.1: +xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.0, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== @@ -16166,11 +15333,6 @@ y18n@^3.2.1: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== -y18n@^5.0.5: - version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" - integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== - yaeti@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" @@ -16199,11 +15361,6 @@ yargs-parser@13.1.2, yargs-parser@^13.1.0, yargs-parser@^13.1.2: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@20.2.4: - version "20.2.4" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" - integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== - yargs-parser@^11.1.1: version "11.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4" @@ -16220,7 +15377,7 @@ yargs-parser@^15.0.1: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^20.2.2, yargs-parser@^20.2.3: +yargs-parser@^20.2.3: version "20.2.9" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== @@ -16241,16 +15398,6 @@ yargs-unparser@1.6.0: lodash "^4.17.15" yargs "^13.3.0" -yargs-unparser@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" - integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== - dependencies: - camelcase "^6.0.0" - decamelize "^4.0.0" - flat "^5.0.2" - is-plain-obj "^2.1.0" - yargs@13.2.4: version "13.2.4" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.2.4.tgz#0b562b794016eb9651b98bd37acf364aa5d6dc83" @@ -16284,19 +15431,6 @@ yargs@13.3.2, yargs@^13.3.0: y18n "^4.0.0" yargs-parser "^13.1.2" -yargs@16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== - dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.0" - y18n "^5.0.5" - yargs-parser "^20.2.2" - yargs@^10.0.3: version "10.1.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-10.1.2.tgz#454d074c2b16a51a43e2fb7807e4f9de69ccb5c5" @@ -16357,8 +15491,3 @@ yauzl@^2.4.2: dependencies: buffer-crc32 "~0.2.3" fd-slicer "~1.1.0" - -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From 8892e1848591b0807cec27b0bb75f9b7c01e4493 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Wed, 16 Oct 2024 17:25:38 +0200 Subject: [PATCH 093/100] ci: fix `verify-bytecode` workflow (#47) * fix: bumb node to 18, update yarn.lock --- .github/workflows/verify-bytecode.yml | 2 +- yarn.lock | 1572 +++++++++++++++++++------ 2 files changed, 1241 insertions(+), 333 deletions(-) diff --git a/.github/workflows/verify-bytecode.yml b/.github/workflows/verify-bytecode.yml index 0ae732d23..79f448502 100644 --- a/.github/workflows/verify-bytecode.yml +++ b/.github/workflows/verify-bytecode.yml @@ -18,7 +18,7 @@ jobs: - name: Install nodejs uses: actions/setup-node@v3 with: - node-version: 14 + node-version: 18 - name: Install dependencies run: yarn diff --git a/yarn.lock b/yarn.lock index 7f9b50f4d..69d31bd39 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,11 @@ # yarn lockfile v1 +"@adraffy/ens-normalize@1.10.0": + version "1.10.0" + resolved "https://registry.yarnpkg.com/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz#d2a39395c587e092d77cbbc80acf956a54f38bf7" + integrity sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q== + "@aragon/contract-helpers-test@0.1.0", "@aragon/contract-helpers-test@^0.1.0": version "0.1.0" resolved "https://registry.yarnpkg.com/@aragon/contract-helpers-test/-/contract-helpers-test-0.1.0.tgz#5c5f09739a0b33ab66843bc4849cb2b997d88af0" @@ -221,30 +226,10 @@ "@babel/helper-validator-identifier" "^7.14.9" to-fast-properties "^2.0.0" -"@ethereumjs/block@^3.4.0", "@ethereumjs/block@^3.5.0", "@ethereumjs/block@^3.5.1": - version "3.5.1" - resolved "https://registry.yarnpkg.com/@ethereumjs/block/-/block-3.5.1.tgz#59737d393503249aa750c37dfc83896234f4e175" - integrity sha512-MoY9bHKABOBK6BW0v1N1Oc0Cve4x/giX67M3TtrVBUsKQTj2eznLGKpydoitxWSZ+WgKKSVhfRMzbCGRwk7T5w== - dependencies: - "@ethereumjs/common" "^2.5.0" - "@ethereumjs/tx" "^3.3.1" - ethereumjs-util "^7.1.1" - merkle-patricia-tree "^4.2.1" - -"@ethereumjs/blockchain@^5.4.0", "@ethereumjs/blockchain@^5.4.1": - version "5.4.2" - resolved "https://registry.yarnpkg.com/@ethereumjs/blockchain/-/blockchain-5.4.2.tgz#5074e0a0157818762a5f5175ea0bd93c5455fe32" - integrity sha512-AOAAwz/lw2lciG9gf5wHi7M/qknraXXnLR66lYgbQ04qfyFC3ZE5x/5rLVm1Vu+kfJLlKrYZTmA0IbOkc7kvgw== - dependencies: - "@ethereumjs/block" "^3.5.1" - "@ethereumjs/common" "^2.5.0" - "@ethereumjs/ethash" "^1.1.0" - debug "^2.2.0" - ethereumjs-util "^7.1.1" - level-mem "^5.0.1" - lru-cache "^5.1.1" - rlp "^2.2.4" - semaphore-async-await "^1.5.1" +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== "@ethereumjs/common@^2.3.0", "@ethereumjs/common@^2.4.0", "@ethereumjs/common@^2.5.0": version "2.5.0" @@ -262,18 +247,12 @@ crc-32 "^1.2.0" ethereumjs-util "^7.1.4" -"@ethereumjs/ethash@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@ethereumjs/ethash/-/ethash-1.1.0.tgz#7c5918ffcaa9cb9c1dc7d12f77ef038c11fb83fb" - integrity sha512-/U7UOKW6BzpA+Vt+kISAoeDie1vAvY4Zy2KF5JJb+So7+1yKmJeJEHOGSnQIj330e9Zyl3L5Nae6VZyh2TJnAA== - dependencies: - "@ethereumjs/block" "^3.5.0" - "@types/levelup" "^4.3.0" - buffer-xor "^2.0.1" - ethereumjs-util "^7.1.1" - miller-rabin "^4.0.0" +"@ethereumjs/rlp@^4.0.1": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@ethereumjs/rlp/-/rlp-4.0.1.tgz#626fabfd9081baab3d0a3074b0c7ecaf674aaa41" + integrity sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw== -"@ethereumjs/tx@^3.2.1", "@ethereumjs/tx@^3.3.0", "@ethereumjs/tx@^3.3.1": +"@ethereumjs/tx@^3.2.1", "@ethereumjs/tx@^3.3.0": version "3.3.2" resolved "https://registry.yarnpkg.com/@ethereumjs/tx/-/tx-3.3.2.tgz#348d4624bf248aaab6c44fec2ae67265efe3db00" integrity sha512-6AaJhwg4ucmwTvw/1qLaZUX5miWrwZ4nLOUsKyb/HtzS3BMw/CasKhdi1ims9mBKeK9sOJCH4qGKOBGyJCeeog== @@ -289,24 +268,14 @@ "@ethereumjs/common" "^2.6.3" ethereumjs-util "^7.1.4" -"@ethereumjs/vm@^5.5.2": - version "5.5.3" - resolved "https://registry.yarnpkg.com/@ethereumjs/vm/-/vm-5.5.3.tgz#dc8b30dd35efb589db093592600207660fa8dada" - integrity sha512-0k5OreWnlgXYs54wohgO11jtGI05GDasj2EYxzuaStxTi15CS3vow5wGYELC1pG9xngE1F/mFmKi/f14XRuDow== +"@ethereumjs/util@^8.1.0": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@ethereumjs/util/-/util-8.1.0.tgz#299df97fb6b034e0577ce9f94c7d9d1004409ed4" + integrity sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA== dependencies: - "@ethereumjs/block" "^3.5.0" - "@ethereumjs/blockchain" "^5.4.1" - "@ethereumjs/common" "^2.5.0" - "@ethereumjs/tx" "^3.3.1" - async-eventemitter "^0.2.4" - core-js-pure "^3.0.1" - debug "^2.2.0" - ethereumjs-util "^7.1.1" - functional-red-black-tree "^1.0.1" - mcl-wasm "^0.7.1" - merkle-patricia-tree "^4.2.1" - rustbn.js "~0.2.0" - util.promisify "^1.0.1" + "@ethereumjs/rlp" "^4.0.1" + ethereum-cryptography "^2.0.0" + micro-ftch "^0.3.1" "@ethersproject/abi@5.0.0-beta.153": version "5.0.0-beta.153" @@ -353,6 +322,21 @@ "@ethersproject/properties" "^5.5.0" "@ethersproject/strings" "^5.5.0" +"@ethersproject/abi@^5.0.9", "@ethersproject/abi@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" + integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== + dependencies: + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/hash" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + "@ethersproject/abstract-provider@5.5.1", "@ethersproject/abstract-provider@^5.5.0": version "5.5.1" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.5.1.tgz#2f1f6e8a3ab7d378d8ad0b5718460f85649710c5" @@ -366,6 +350,19 @@ "@ethersproject/transactions" "^5.5.0" "@ethersproject/web" "^5.5.0" +"@ethersproject/abstract-provider@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz#b0a8550f88b6bf9d51f90e4795d48294630cb9ef" + integrity sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/networks" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/transactions" "^5.7.0" + "@ethersproject/web" "^5.7.0" + "@ethersproject/abstract-signer@5.5.0", "@ethersproject/abstract-signer@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.5.0.tgz#590ff6693370c60ae376bf1c7ada59eb2a8dd08d" @@ -377,6 +374,17 @@ "@ethersproject/logger" "^5.5.0" "@ethersproject/properties" "^5.5.0" +"@ethersproject/abstract-signer@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz#13f4f32117868452191a4649723cb086d2b596b2" + integrity sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ== + dependencies: + "@ethersproject/abstract-provider" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/address@5.5.0", "@ethersproject/address@>=5.0.0-beta.128", "@ethersproject/address@^5.0.2", "@ethersproject/address@^5.0.4", "@ethersproject/address@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.5.0.tgz#bcc6f576a553f21f3dd7ba17248f81b473c9c78f" @@ -388,6 +396,17 @@ "@ethersproject/logger" "^5.5.0" "@ethersproject/rlp" "^5.5.0" +"@ethersproject/address@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37" + integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + "@ethersproject/base64@5.5.0", "@ethersproject/base64@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.5.0.tgz#881e8544e47ed976930836986e5eb8fab259c090" @@ -395,6 +414,13 @@ dependencies: "@ethersproject/bytes" "^5.5.0" +"@ethersproject/base64@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.7.0.tgz#ac4ee92aa36c1628173e221d0d01f53692059e1c" + integrity sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/basex@5.5.0", "@ethersproject/basex@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.5.0.tgz#e40a53ae6d6b09ab4d977bd037010d4bed21b4d3" @@ -412,6 +438,15 @@ "@ethersproject/logger" "^5.5.0" bn.js "^4.11.9" +"@ethersproject/bignumber@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.7.0.tgz#e2f03837f268ba655ffba03a57853e18a18dc9c2" + integrity sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + bn.js "^5.2.1" + "@ethersproject/bytes@5.5.0", "@ethersproject/bytes@>=5.0.0-beta.129", "@ethersproject/bytes@^5.0.4", "@ethersproject/bytes@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.5.0.tgz#cb11c526de657e7b45d2e0f0246fb3b9d29a601c" @@ -419,6 +454,13 @@ dependencies: "@ethersproject/logger" "^5.5.0" +"@ethersproject/bytes@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d" + integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A== + dependencies: + "@ethersproject/logger" "^5.7.0" + "@ethersproject/constants@5.5.0", "@ethersproject/constants@>=5.0.0-beta.128", "@ethersproject/constants@^5.0.4", "@ethersproject/constants@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.5.0.tgz#d2a2cd7d94bd1d58377d1d66c4f53c9be4d0a45e" @@ -426,6 +468,13 @@ dependencies: "@ethersproject/bignumber" "^5.5.0" +"@ethersproject/constants@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.7.0.tgz#df80a9705a7e08984161f09014ea012d1c75295e" + integrity sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/contracts@5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.5.0.tgz#b735260d4bd61283a670a82d5275e2a38892c197" @@ -456,6 +505,21 @@ "@ethersproject/properties" "^5.5.0" "@ethersproject/strings" "^5.5.0" +"@ethersproject/hash@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7" + integrity sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g== + dependencies: + "@ethersproject/abstract-signer" "^5.7.0" + "@ethersproject/address" "^5.7.0" + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + "@ethersproject/hdnode@5.5.0", "@ethersproject/hdnode@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.5.0.tgz#4a04e28f41c546f7c978528ea1575206a200ddf6" @@ -501,11 +565,24 @@ "@ethersproject/bytes" "^5.5.0" js-sha3 "0.8.0" +"@ethersproject/keccak256@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.7.0.tgz#3186350c6e1cd6aba7940384ec7d6d9db01f335a" + integrity sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + js-sha3 "0.8.0" + "@ethersproject/logger@5.5.0", "@ethersproject/logger@>=5.0.0-beta.129", "@ethersproject/logger@^5.0.5", "@ethersproject/logger@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.5.0.tgz#0c2caebeff98e10aefa5aef27d7441c7fd18cf5d" integrity sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg== +"@ethersproject/logger@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892" + integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig== + "@ethersproject/networks@5.5.0", "@ethersproject/networks@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.5.0.tgz#babec47cab892c51f8dd652ce7f2e3e14283981a" @@ -513,6 +590,13 @@ dependencies: "@ethersproject/logger" "^5.5.0" +"@ethersproject/networks@^5.7.0": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.7.1.tgz#118e1a981d757d45ccea6bb58d9fd3d9db14ead6" + integrity sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ== + dependencies: + "@ethersproject/logger" "^5.7.0" + "@ethersproject/pbkdf2@5.5.0", "@ethersproject/pbkdf2@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.5.0.tgz#e25032cdf02f31505d47afbf9c3e000d95c4a050" @@ -528,6 +612,13 @@ dependencies: "@ethersproject/logger" "^5.5.0" +"@ethersproject/properties@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.7.0.tgz#a6e12cb0439b878aaf470f1902a176033067ed30" + integrity sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw== + dependencies: + "@ethersproject/logger" "^5.7.0" + "@ethersproject/providers@5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.5.0.tgz#bc2876a8fe5e0053ed9828b1f3767ae46e43758b" @@ -569,6 +660,14 @@ "@ethersproject/bytes" "^5.5.0" "@ethersproject/logger" "^5.5.0" +"@ethersproject/rlp@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.7.0.tgz#de39e4d5918b9d74d46de93af80b7685a9c21304" + integrity sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/sha2@5.5.0", "@ethersproject/sha2@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.5.0.tgz#a40a054c61f98fd9eee99af2c3cc6ff57ec24db7" @@ -590,6 +689,18 @@ elliptic "6.5.4" hash.js "1.1.7" +"@ethersproject/signing-key@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.7.0.tgz#06b2df39411b00bc57c7c09b01d1e41cf1b16ab3" + integrity sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + bn.js "^5.2.1" + elliptic "6.5.4" + hash.js "1.1.7" + "@ethersproject/solidity@5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.5.0.tgz#2662eb3e5da471b85a20531e420054278362f93f" @@ -611,6 +722,15 @@ "@ethersproject/constants" "^5.5.0" "@ethersproject/logger" "^5.5.0" +"@ethersproject/strings@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.7.0.tgz#54c9d2a7c57ae8f1205c88a9d3a56471e14d5ed2" + integrity sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg== + dependencies: + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/transactions@5.5.0", "@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.5.0.tgz#7e9bf72e97bcdf69db34fe0d59e2f4203c7a2908" @@ -626,6 +746,21 @@ "@ethersproject/rlp" "^5.5.0" "@ethersproject/signing-key" "^5.5.0" +"@ethersproject/transactions@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.7.0.tgz#91318fc24063e057885a6af13fdb703e1f993d3b" + integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ== + dependencies: + "@ethersproject/address" "^5.7.0" + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/keccak256" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/rlp" "^5.7.0" + "@ethersproject/signing-key" "^5.7.0" + "@ethersproject/units@5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.5.0.tgz#104d02db5b5dc42cc672cc4587bafb87a95ee45e" @@ -635,6 +770,15 @@ "@ethersproject/constants" "^5.5.0" "@ethersproject/logger" "^5.5.0" +"@ethersproject/units@^5.7.0": + version "5.7.0" + resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.7.0.tgz#637b563d7e14f42deeee39245275d477aae1d8b1" + integrity sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg== + dependencies: + "@ethersproject/bignumber" "^5.7.0" + "@ethersproject/constants" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/wallet@5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.5.0.tgz#322a10527a440ece593980dca6182f17d54eae75" @@ -667,6 +811,17 @@ "@ethersproject/properties" "^5.5.0" "@ethersproject/strings" "^5.5.0" +"@ethersproject/web@^5.7.0": + version "5.7.1" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.7.1.tgz#de1f285b373149bee5928f4eb7bcb87ee5fbb4ae" + integrity sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w== + dependencies: + "@ethersproject/base64" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/logger" "^5.7.0" + "@ethersproject/properties" "^5.7.0" + "@ethersproject/strings" "^5.7.0" + "@ethersproject/wordlists@5.5.0", "@ethersproject/wordlists@^5.5.0": version "5.5.0" resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.5.0.tgz#aac74963aa43e643638e5172353d931b347d584f" @@ -752,6 +907,23 @@ unique-filename "^1.1.1" which "^1.3.1" +"@fastify/busboy@^2.0.0": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@fastify/busboy/-/busboy-2.1.1.tgz#b9da6a878a371829a0502c9b6c1c143ef6663f4d" + integrity sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA== + +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + "@lerna/add@3.21.0": version "3.21.0" resolved "https://registry.yarnpkg.com/@lerna/add/-/add-3.21.0.tgz#27007bde71cc7b0a2969ab3c2f0ae41578b4577b" @@ -1437,6 +1609,17 @@ npmlog "^4.1.2" write-file-atomic "^2.3.0" +"@metamask/eth-sig-util@^4.0.0": + version "4.0.1" + resolved "https://registry.yarnpkg.com/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz#3ad61f6ea9ad73ba5b19db780d40d9aae5157088" + integrity sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ== + dependencies: + ethereumjs-abi "^0.6.8" + ethereumjs-util "^6.2.1" + ethjs-util "^0.1.6" + tweetnacl "^1.0.3" + tweetnacl-util "^0.15.1" + "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" @@ -1450,6 +1633,45 @@ resolved "https://registry.yarnpkg.com/@multiformats/base-x/-/base-x-4.0.1.tgz#95ff0fa58711789d53aefb2590a8b7a4e715d121" integrity sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw== +"@noble/curves@1.2.0", "@noble/curves@~1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.2.0.tgz#92d7e12e4e49b23105a2555c6984d41733d65c35" + integrity sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw== + dependencies: + "@noble/hashes" "1.3.2" + +"@noble/curves@1.4.2", "@noble/curves@~1.4.0": + version "1.4.2" + resolved "https://registry.yarnpkg.com/@noble/curves/-/curves-1.4.2.tgz#40309198c76ed71bc6dbf7ba24e81ceb4d0d1fe9" + integrity sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw== + dependencies: + "@noble/hashes" "1.4.0" + +"@noble/hashes@1.2.0", "@noble/hashes@~1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12" + integrity sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ== + +"@noble/hashes@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.2.tgz#6f26dbc8fbc7205873ce3cee2f690eba0d421b39" + integrity sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ== + +"@noble/hashes@1.4.0", "@noble/hashes@~1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.4.0.tgz#45814aa329f30e4fe0ba49426f49dfccdd066426" + integrity sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg== + +"@noble/hashes@~1.3.0", "@noble/hashes@~1.3.2": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.3.3.tgz#39908da56a4adc270147bb07968bf3b16cfe1699" + integrity sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA== + +"@noble/secp256k1@1.7.1", "@noble/secp256k1@~1.7.0": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" + integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -1476,6 +1698,139 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@nomicfoundation/edr-darwin-arm64@0.6.3": + version "0.6.3" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-arm64/-/edr-darwin-arm64-0.6.3.tgz#7f94f80f25bbf8f15421aca0626b1e243c5b6fba" + integrity sha512-hqtI7tYDqKG5PDmZ//Z65EH5cgH8VL/SAAu50rpHP7WAVfJWkOCcYbecywwF6nhHdonJbRTDGAeG1/+VOy6zew== + +"@nomicfoundation/edr-darwin-x64@0.6.3": + version "0.6.3" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-darwin-x64/-/edr-darwin-x64-0.6.3.tgz#57cbbe09c70480e7eb79273ba5a497327d72347b" + integrity sha512-4fGi79/lyOlRUORhCYsYb3sWqRHuHT7qqzyZfZuNOn8llaxmT1k36xNmvpyg37R8SzjnhT/DzoukSJrs23Ip9Q== + +"@nomicfoundation/edr-linux-arm64-gnu@0.6.3": + version "0.6.3" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-gnu/-/edr-linux-arm64-gnu-0.6.3.tgz#122f5ec8b00297e9ed0111405c8779a3c3ba26f3" + integrity sha512-yFFTvGFMhfAvQ1Z2itUh1jpoUA+mVROyVELcaxjIq8fyg602lQmbS+NXkhQ+oaeDgJ+06mSENrHBg4fcfRf9cw== + +"@nomicfoundation/edr-linux-arm64-musl@0.6.3": + version "0.6.3" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-arm64-musl/-/edr-linux-arm64-musl-0.6.3.tgz#2b0371371540373b10521ead4ffa70a2d9e6ac8e" + integrity sha512-pOKmd0Fa3a6BHg5qbjbl/jMRELVi9oazbfiuU7Bvgn/dpTK+ID3jwT0SXiuC2zxjmPByWgXL6G9XRf5BPAM2rQ== + +"@nomicfoundation/edr-linux-x64-gnu@0.6.3": + version "0.6.3" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-gnu/-/edr-linux-x64-gnu-0.6.3.tgz#63849575eddbcd7a5da581d401fba6f5f9347644" + integrity sha512-3AUferhkLIXtLV63w5GjpHttzdxZ36i656XMy+pkBZbbiqnzIVeKWg6DJv1A94fQY16gB4gqj9CLq4CWvbNN6w== + +"@nomicfoundation/edr-linux-x64-musl@0.6.3": + version "0.6.3" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-linux-x64-musl/-/edr-linux-x64-musl-0.6.3.tgz#3b5e6462f47b40cde81bafc6da003c58b2eb9839" + integrity sha512-fr6bD872WIBXe9YnTDi0CzYepMcYRgSnkVqn0yK4wRnIvKrloWhxXNVY45GVIl51aNZguBnvoA4WEt6HIazs3A== + +"@nomicfoundation/edr-win32-x64-msvc@0.6.3": + version "0.6.3" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr-win32-x64-msvc/-/edr-win32-x64-msvc-0.6.3.tgz#45be7ba94b950e78e862cb3af0c320e070e0e452" + integrity sha512-sn34MvN1ajw2Oq1+Drpxej78Z0HfIzI4p4WlolupAV9dOZKzp2JAIQeLVfZpjIFbF3zuyxLPP4dUBrQoFPEqhA== + +"@nomicfoundation/edr@^0.6.3": + version "0.6.3" + resolved "https://registry.yarnpkg.com/@nomicfoundation/edr/-/edr-0.6.3.tgz#47f1b217ce5eb09aef419d76a8488bb77cd88b94" + integrity sha512-hThe5ORR75WFYTXKL0K2AyLDxkTMrG+VQ1yL9BhQYsuh3OIH+3yNDxMz2LjfvrpOrMmJ4kk5NKdFewpqDojjXQ== + dependencies: + "@nomicfoundation/edr-darwin-arm64" "0.6.3" + "@nomicfoundation/edr-darwin-x64" "0.6.3" + "@nomicfoundation/edr-linux-arm64-gnu" "0.6.3" + "@nomicfoundation/edr-linux-arm64-musl" "0.6.3" + "@nomicfoundation/edr-linux-x64-gnu" "0.6.3" + "@nomicfoundation/edr-linux-x64-musl" "0.6.3" + "@nomicfoundation/edr-win32-x64-msvc" "0.6.3" + +"@nomicfoundation/ethereumjs-common@4.0.4": + version "4.0.4" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-4.0.4.tgz#9901f513af2d4802da87c66d6f255b510bef5acb" + integrity sha512-9Rgb658lcWsjiicr5GzNCjI1llow/7r0k50dLL95OJ+6iZJcVbi15r3Y0xh2cIO+zgX0WIHcbzIu6FeQf9KPrg== + dependencies: + "@nomicfoundation/ethereumjs-util" "9.0.4" + +"@nomicfoundation/ethereumjs-rlp@5.0.4": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-5.0.4.tgz#66c95256fc3c909f6fb18f6a586475fc9762fa30" + integrity sha512-8H1S3s8F6QueOc/X92SdrA4RDenpiAEqMg5vJH99kcQaCy/a3Q6fgseo75mgWlbanGJXSlAPtnCeG9jvfTYXlw== + +"@nomicfoundation/ethereumjs-tx@5.0.4": + version "5.0.4" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-5.0.4.tgz#b0ceb58c98cc34367d40a30d255d6315b2f456da" + integrity sha512-Xjv8wAKJGMrP1f0n2PeyfFCCojHd7iS3s/Ab7qzF1S64kxZ8Z22LCMynArYsVqiFx6rzYy548HNVEyI+AYN/kw== + dependencies: + "@nomicfoundation/ethereumjs-common" "4.0.4" + "@nomicfoundation/ethereumjs-rlp" "5.0.4" + "@nomicfoundation/ethereumjs-util" "9.0.4" + ethereum-cryptography "0.1.3" + +"@nomicfoundation/ethereumjs-util@9.0.4": + version "9.0.4" + resolved "https://registry.yarnpkg.com/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-9.0.4.tgz#84c5274e82018b154244c877b76bc049a4ed7b38" + integrity sha512-sLOzjnSrlx9Bb9EFNtHzK/FJFsfg2re6bsGqinFinH1gCqVfz9YYlXiMWwDM4C/L4ywuHFCYwfKTVr/QHQcU0Q== + dependencies: + "@nomicfoundation/ethereumjs-rlp" "5.0.4" + ethereum-cryptography "0.1.3" + +"@nomicfoundation/hardhat-network-helpers@^1.0.10": + version "1.0.12" + resolved "https://registry.yarnpkg.com/@nomicfoundation/hardhat-network-helpers/-/hardhat-network-helpers-1.0.12.tgz#2c0abec0c50b75f9d0d71776e49e3b5ef746d289" + integrity sha512-xTNQNI/9xkHvjmCJnJOTyqDSl8uq1rKb2WOVmixQxFtRd7Oa3ecO8zM0cyC2YmOK+jHB9WPZ+F/ijkHg1CoORA== + dependencies: + ethereumjs-util "^7.1.4" + +"@nomicfoundation/solidity-analyzer-darwin-arm64@0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.2.tgz#3a9c3b20d51360b20affb8f753e756d553d49557" + integrity sha512-JaqcWPDZENCvm++lFFGjrDd8mxtf+CtLd2MiXvMNTBD33dContTZ9TWETwNFwg7JTJT5Q9HEecH7FA+HTSsIUw== + +"@nomicfoundation/solidity-analyzer-darwin-x64@0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-darwin-x64/-/solidity-analyzer-darwin-x64-0.1.2.tgz#74dcfabeb4ca373d95bd0d13692f44fcef133c28" + integrity sha512-fZNmVztrSXC03e9RONBT+CiksSeYcxI1wlzqyr0L7hsQlK1fzV+f04g2JtQ1c/Fe74ZwdV6aQBdd6Uwl1052sw== + +"@nomicfoundation/solidity-analyzer-linux-arm64-gnu@0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-arm64-gnu/-/solidity-analyzer-linux-arm64-gnu-0.1.2.tgz#4af5849a89e5a8f511acc04f28eb5d4460ba2b6a" + integrity sha512-3d54oc+9ZVBuB6nbp8wHylk4xh0N0Gc+bk+/uJae+rUgbOBwQSfuGIbAZt1wBXs5REkSmynEGcqx6DutoK0tPA== + +"@nomicfoundation/solidity-analyzer-linux-arm64-musl@0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-arm64-musl/-/solidity-analyzer-linux-arm64-musl-0.1.2.tgz#54036808a9a327b2ff84446c130a6687ee702a8e" + integrity sha512-iDJfR2qf55vgsg7BtJa7iPiFAsYf2d0Tv/0B+vhtnI16+wfQeTbP7teookbGvAo0eJo7aLLm0xfS/GTkvHIucA== + +"@nomicfoundation/solidity-analyzer-linux-x64-gnu@0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-x64-gnu/-/solidity-analyzer-linux-x64-gnu-0.1.2.tgz#466cda0d6e43691986c944b909fc6dbb8cfc594e" + integrity sha512-9dlHMAt5/2cpWyuJ9fQNOUXFB/vgSFORg1jpjX1Mh9hJ/MfZXlDdHQ+DpFCs32Zk5pxRBb07yGvSHk9/fezL+g== + +"@nomicfoundation/solidity-analyzer-linux-x64-musl@0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-linux-x64-musl/-/solidity-analyzer-linux-x64-musl-0.1.2.tgz#2b35826987a6e94444140ac92310baa088ee7f94" + integrity sha512-GzzVeeJob3lfrSlDKQw2bRJ8rBf6mEYaWY+gW0JnTDHINA0s2gPR4km5RLIj1xeZZOYz4zRw+AEeYgLRqB2NXg== + +"@nomicfoundation/solidity-analyzer-win32-x64-msvc@0.1.2": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer-win32-x64-msvc/-/solidity-analyzer-win32-x64-msvc-0.1.2.tgz#e6363d13b8709ca66f330562337dbc01ce8bbbd9" + integrity sha512-Fdjli4DCcFHb4Zgsz0uEJXZ2K7VEO+w5KVv7HmT7WO10iODdU9csC2az4jrhEsRtiR9Gfd74FlG0NYlw1BMdyA== + +"@nomicfoundation/solidity-analyzer@^0.1.0": + version "0.1.2" + resolved "https://registry.yarnpkg.com/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.2.tgz#8bcea7d300157bf3a770a851d9f5c5e2db34ac55" + integrity sha512-q4n32/FNKIhQ3zQGGw5CvPF6GTvDCpYwIf7bEY/dZTZbgfDsHyjJwURxUJf3VQuuJj+fDIFl4+KkBVbw4Ef6jA== + optionalDependencies: + "@nomicfoundation/solidity-analyzer-darwin-arm64" "0.1.2" + "@nomicfoundation/solidity-analyzer-darwin-x64" "0.1.2" + "@nomicfoundation/solidity-analyzer-linux-arm64-gnu" "0.1.2" + "@nomicfoundation/solidity-analyzer-linux-arm64-musl" "0.1.2" + "@nomicfoundation/solidity-analyzer-linux-x64-gnu" "0.1.2" + "@nomicfoundation/solidity-analyzer-linux-x64-musl" "0.1.2" + "@nomicfoundation/solidity-analyzer-win32-x64-msvc" "0.1.2" + "@nomiclabs/buidler-ganache@^1.3.3": version "1.3.3" resolved "https://registry.yarnpkg.com/@nomiclabs/buidler-ganache/-/buidler-ganache-1.3.3.tgz#220beb381b0df45bf1860396df4a639267711066" @@ -1579,18 +1934,21 @@ resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-ethers/-/hardhat-ethers-2.0.2.tgz#c472abcba0c5185aaa4ad4070146e95213c68511" integrity sha512-6quxWe8wwS4X5v3Au8q1jOvXYEPkS1Fh+cME5u6AwNdnI4uERvPlVjlgRWzpnb+Rrt1l/cEqiNRH9GlsBMSDQg== -"@nomiclabs/hardhat-etherscan@^2.1.6": - version "2.1.6" - resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-2.1.6.tgz#8d1502f42adc6f7b8ef16fb917c0b5a8780cb83a" - integrity sha512-gCvT5fj8GbXS9+ACS3BzrX0pzYHHZqAHCb+NcipOkl2cy48FakUXlzrCf4P4sTH+Y7W10OgT62ezD1sJ+/NikQ== +"@nomiclabs/hardhat-etherscan@^3.1.8": + version "3.1.8" + resolved "https://registry.yarnpkg.com/@nomiclabs/hardhat-etherscan/-/hardhat-etherscan-3.1.8.tgz#3c12ee90b3733e0775e05111146ef9418d4f5a38" + integrity sha512-v5F6IzQhrsjHh6kQz4uNrym49brK9K5bYCq2zQZ729RYRaifI9hHbtmK+KkIVevfhut7huQFEQ77JLRMAzWYjQ== dependencies: "@ethersproject/abi" "^5.1.2" "@ethersproject/address" "^5.0.2" - cbor "^5.0.2" + cbor "^8.1.0" + chalk "^2.4.2" debug "^4.1.1" fs-extra "^7.0.1" - node-fetch "^2.6.0" + lodash "^4.17.11" semver "^6.3.0" + table "^6.8.0" + undici "^5.14.0" "@nomiclabs/hardhat-ganache@^2.0.1": version "2.0.1" @@ -1746,6 +2104,11 @@ dependencies: "@octokit/openapi-types" "^11.2.0" +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" @@ -1833,6 +2196,62 @@ debug "^3.1.0" hosted-git-info "^2.6.0" +"@scure/base@~1.1.0", "@scure/base@~1.1.2", "@scure/base@~1.1.6": + version "1.1.9" + resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.9.tgz#e5e142fbbfe251091f9c5f1dd4c834ac04c3dbd1" + integrity sha512-8YKhl8GHiNI/pU2VMaofa2Tor7PJRAjwQLBBuilkJ9L5+13yVbC7JO/wS7piioAvPSwR3JKM1IJ/u4xQzbcXKg== + +"@scure/bip32@1.1.5": + version "1.1.5" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.1.5.tgz#d2ccae16dcc2e75bc1d75f5ef3c66a338d1ba300" + integrity sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw== + dependencies: + "@noble/hashes" "~1.2.0" + "@noble/secp256k1" "~1.7.0" + "@scure/base" "~1.1.0" + +"@scure/bip32@1.3.2": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.3.2.tgz#90e78c027d5e30f0b22c1f8d50ff12f3fb7559f8" + integrity sha512-N1ZhksgwD3OBlwTv3R6KFEcPojl/W4ElJOeCZdi+vuI5QmTFwLq3OFf2zd2ROpKvxFdgZ6hUpb0dx9bVNEwYCA== + dependencies: + "@noble/curves" "~1.2.0" + "@noble/hashes" "~1.3.2" + "@scure/base" "~1.1.2" + +"@scure/bip32@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@scure/bip32/-/bip32-1.4.0.tgz#4e1f1e196abedcef395b33b9674a042524e20d67" + integrity sha512-sVUpc0Vq3tXCkDGYVWGIZTRfnvu8LoTDaev7vbwh0omSvVORONr960MQWdKqJDCReIEmTj3PAr73O3aoxz7OPg== + dependencies: + "@noble/curves" "~1.4.0" + "@noble/hashes" "~1.4.0" + "@scure/base" "~1.1.6" + +"@scure/bip39@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.1.1.tgz#b54557b2e86214319405db819c4b6a370cf340c5" + integrity sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg== + dependencies: + "@noble/hashes" "~1.2.0" + "@scure/base" "~1.1.0" + +"@scure/bip39@1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.2.1.tgz#5cee8978656b272a917b7871c981e0541ad6ac2a" + integrity sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg== + dependencies: + "@noble/hashes" "~1.3.0" + "@scure/base" "~1.1.0" + +"@scure/bip39@1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@scure/bip39/-/bip39-1.3.0.tgz#0f258c16823ddd00739461ac31398b4e7d6a18c3" + integrity sha512-disdg7gHuTDZtY+ZdkmLpPCk7fxZSu3gBiEGuoC1XYxv9cGx3Z6cpTggCgW6odSOOIXCiDjuGejW+aJKCY/pIQ== + dependencies: + "@noble/hashes" "~1.4.0" + "@scure/base" "~1.1.6" + "@sentry/core@5.30.0": version "5.30.0" resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.30.0.tgz#6b203664f69e75106ee8b5a2fe1d717379b331f3" @@ -1911,11 +2330,6 @@ resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd" integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== -"@solidity-parser/parser@^0.11.0": - version "0.11.1" - resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.11.1.tgz#fa840af64840c930f24a9c82c08d4a092a068add" - integrity sha512-H8BSBoKE8EubJa0ONqecA2TviT3TnHeC4NpgnAHSUiuhZoQBfPB4L2P9bs8R6AoTW10Endvh3vc+fomVMIDIYQ== - "@solidity-parser/parser@^0.12.0", "@solidity-parser/parser@^0.12.2": version "0.12.2" resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.12.2.tgz#1afad367cb29a2ed8cdd4a3a62701c2821fb578f" @@ -1928,12 +2342,10 @@ dependencies: antlr4ts "^0.5.0-alpha.4" -"@solidity-parser/parser@^0.14.0": - version "0.14.1" - resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.14.1.tgz#179afb29f4e295a77cc141151f26b3848abc3c46" - integrity sha512-eLjj2L6AuQjBB6s/ibwCAc0DwrR5Ge+ys+wgWo+bviU7fV2nTMQhU63CGaDKXg9iTmMxwhkyoggdIR7ZGRfMgw== - dependencies: - antlr4ts "^0.5.0-alpha.4" +"@solidity-parser/parser@^0.18.0": + version "0.18.0" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.18.0.tgz#8e77a02a09ecce957255a2f48c9a7178ec191908" + integrity sha512-yfORGUIPgLck41qyN7nbwJRAx17/jAIXCTanHOJZhB6PJ1iAk/84b/xlsVKFSyNyLXIj0dhppoE0+CRws7wlzA== "@solidity-parser/parser@^0.5.2": version "0.5.2" @@ -2151,11 +2563,6 @@ xhr "^2.2.0" xtend "^4.0.1" -"@types/abstract-leveldown@*": - version "5.0.2" - resolved "https://registry.yarnpkg.com/@types/abstract-leveldown/-/abstract-leveldown-5.0.2.tgz#ee81917fe38f770e29eec8139b6f16ee4a8b0a5f" - integrity sha512-+jA1XXF3jsz+Z7FcuiNqgK53hTa/luglT2TyTpKPqoYbxVY+mCPF22Rm+q3KPBrMHJwNXFrTViHszBOfU4vftQ== - "@types/bignumber.js@^5.0.0": version "5.0.0" resolved "https://registry.yarnpkg.com/@types/bignumber.js/-/bignumber.js-5.0.0.tgz#d9f1a378509f3010a3255e9cc822ad0eeb4ab969" @@ -2204,20 +2611,6 @@ "@types/minimatch" "*" "@types/node" "*" -"@types/level-errors@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/level-errors/-/level-errors-3.0.0.tgz#15c1f4915a5ef763b51651b15e90f6dc081b96a8" - integrity sha512-/lMtoq/Cf/2DVOm6zE6ORyOM+3ZVm/BvzEZVxUhf6bgh8ZHglXlBqxbxSlJeVp8FCbD3IVvk/VbsaNmDjrQvqQ== - -"@types/levelup@^4.3.0": - version "4.3.3" - resolved "https://registry.yarnpkg.com/@types/levelup/-/levelup-4.3.3.tgz#4dc2b77db079b1cf855562ad52321aa4241b8ef4" - integrity sha512-K+OTIjJcZHVlZQN1HmU64VtrC0jC3dXWQozuEIR9zVvltIk90zaGPM2AgT+fIkChpzHhFE3YnvFLCbLtzAmexA== - dependencies: - "@types/abstract-leveldown" "*" - "@types/level-errors" "*" - "@types/node" "*" - "@types/long@^4.0.1": version "4.0.1" resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.1.tgz#459c65fa1867dafe6a8f322c4c51695663cc55e9" @@ -2314,6 +2707,11 @@ abbrev@1.0.x: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" integrity sha1-kbR5JYinc4wl813W9jdSovh3YTU= +abitype@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/abitype/-/abitype-1.0.0.tgz#237176dace81d90d018bebf3a45cb42f2a2d9e97" + integrity sha512-NMeMah//6bJ56H5XRj8QCV4AwuW6hB6zqz2LnhhLdcWVQOsXki6/Pn3APeqxCma62nXIcmZWdu1DlHWS74umVQ== + abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" @@ -2342,17 +2740,6 @@ abstract-leveldown@^5.0.0, abstract-leveldown@~5.0.0: dependencies: xtend "~4.0.0" -abstract-leveldown@^6.2.1: - version "6.3.0" - resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.3.0.tgz#d25221d1e6612f820c35963ba4bd739928f6026a" - integrity sha512-TU5nlYgta8YrBMNpc9FwQzRbiXsj49gsALsXadbGHt9CROPzX5fB0rWDR5mtdpOOKa5XqRFpbj1QroPAoPzVjQ== - dependencies: - buffer "^5.5.0" - immediate "^3.2.3" - level-concat-iterator "~2.0.0" - level-supports "~1.0.0" - xtend "~4.0.0" - abstract-leveldown@~2.6.0: version "2.6.3" resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-2.6.3.tgz#1c5e8c6a5ef965ae8c35dfb3a8770c476b82c4b8" @@ -2360,17 +2747,6 @@ abstract-leveldown@~2.6.0: dependencies: xtend "~4.0.0" -abstract-leveldown@~6.2.1: - version "6.2.3" - resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-6.2.3.tgz#036543d87e3710f2528e47040bc3261b77a9a8eb" - integrity sha512-BsLm5vFMRUrrLeCcRc+G0t2qOaTzpoJQLOubq2XM72eNpjF5UdU5o/5NvlNhx95XHcAvcl8OMXr4mlg/fRgUXQ== - dependencies: - buffer "^5.5.0" - immediate "^3.2.3" - level-concat-iterator "~2.0.0" - level-supports "~1.0.0" - xtend "~4.0.0" - accepts@~1.3.7: version "1.3.7" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" @@ -2427,6 +2803,14 @@ agentkeepalive@^3.4.1: dependencies: humanize-ms "^1.2.1" +aggregate-error@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" + integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== + dependencies: + clean-stack "^2.0.0" + indent-string "^4.0.0" + ajv@^5.2.2: version "5.5.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" @@ -2447,11 +2831,28 @@ ajv@^6.10.0, ajv@^6.12.3: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^8.0.1: + version "8.17.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.17.1.tgz#37d9a5c776af6bc92d7f4f9510eba4c0a60d11a6" + integrity sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + amdefine@>=0.0.4: version "1.0.1" resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= +ansi-align@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" + integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== + dependencies: + string-width "^4.1.0" + ansi-colors@3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" @@ -2462,6 +2863,11 @@ ansi-colors@^4.1.0, ansi-colors@^4.1.1: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== +ansi-colors@^4.1.3: + version "4.1.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" + integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== + ansi-escapes@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" @@ -2505,6 +2911,11 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-regex@^6.0.1: + version "6.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" + integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== + ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" @@ -2517,13 +2928,18 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" -ansi-styles@^4.1.0: +ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + antlr4ts@^0.5.0-alpha.4: version "0.5.0-alpha.4" resolved "https://registry.yarnpkg.com/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz#71702865a87478ed0b40c0709f422cf14d51652a" @@ -2597,6 +3013,11 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + arr-diff@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" @@ -2651,7 +3072,7 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -array-uniq@1.0.3, array-uniq@^1.0.1, array-uniq@^1.0.3: +array-uniq@^1.0.1, array-uniq@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= @@ -2708,12 +3129,17 @@ assign-symbols@^1.0.0: resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + async-each@^1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== -async-eventemitter@^0.2.2, async-eventemitter@^0.2.4: +async-eventemitter@^0.2.2: version "0.2.4" resolved "https://registry.yarnpkg.com/async-eventemitter/-/async-eventemitter-0.2.4.tgz#f5e7c8ca7d3e46aab9ec40a292baf686a0bafaca" integrity sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw== @@ -2779,6 +3205,15 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== +axios@^1.6.7: + version "1.7.7" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.7.tgz#2f554296f9892a72ac8d8e4c5b79c14a91d0a47f" + integrity sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" @@ -3381,7 +3816,7 @@ big.js@^5.2.2: resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== -bignumber.js@*, bignumber.js@^9.0.0, bignumber.js@^9.0.1: +bignumber.js@*, bignumber.js@^9.0.0: version "9.0.1" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5" integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA== @@ -3480,6 +3915,11 @@ bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2, bn.js@^5.1.3, bn.js@^5.2.0: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== +bn.js@^5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" + integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== + body-parser@1.19.0, body-parser@^1.16.0: version "1.19.0" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" @@ -3527,6 +3967,20 @@ borc@^3.0.0: json-text-sequence "~0.3.0" readable-stream "^3.6.0" +boxen@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" + integrity sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ== + dependencies: + ansi-align "^3.0.0" + camelcase "^6.2.0" + chalk "^4.1.0" + cli-boxes "^2.2.1" + string-width "^4.2.2" + type-fest "^0.20.2" + widest-line "^3.1.0" + wrap-ansi "^7.0.0" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -3535,6 +3989,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + braces@^1.8.2: version "1.8.5" resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" @@ -3572,6 +4033,11 @@ brorand@^1.0.1, brorand@^1.1.0: resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= +brotli-wasm@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brotli-wasm/-/brotli-wasm-2.0.1.tgz#2b3f4dc3db0c3e60d2635c055e6bac4ddf4bd3f5" + integrity sha512-+3USgYsC7bzb5yU0/p2HnnynZl0ak0E6uoIm4UW4Aby/8s8HFCq6NCfrrf1E9c3O8OCSzq3oYO1tUVqIi61Nww== + browser-readablestream-to-it@^1.0.1, browser-readablestream-to-it@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/browser-readablestream-to-it/-/browser-readablestream-to-it-1.0.3.tgz#ac3e406c7ee6cdf0a502dd55db33bab97f7fba76" @@ -3582,7 +4048,7 @@ browser-stdout@1.3.0: resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.0.tgz#f351d32969d32fa5d7a5567154263d928ae3bd1f" integrity sha1-81HTKWnTL6XXpVZxVCY9korjvR8= -browser-stdout@1.3.1: +browser-stdout@1.3.1, browser-stdout@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== @@ -3953,6 +4419,11 @@ camelcase@^5.0.0, camelcase@^5.3.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== +camelcase@^6.0.0, camelcase@^6.2.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30001265: version "1.0.30001270" resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001270.tgz#cc9c37a4ec5c1a8d616fc7bace902bb053b0cdea" @@ -3973,13 +4444,12 @@ caw@^2.0.1: tunnel-agent "^0.6.0" url-to-options "^1.0.1" -cbor@^5.0.2: - version "5.2.0" - resolved "https://registry.yarnpkg.com/cbor/-/cbor-5.2.0.tgz#4cca67783ccd6de7b50ab4ed62636712f287a67c" - integrity sha512-5IMhi9e1QU76ppa5/ajP1BmMWZ2FHkhAhjeVKQ/EFCgYSEaeVaoGtL7cxJskf9oCCk+XjzaIdc3IuU/dbA/o2A== +cbor@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-8.1.0.tgz#cfc56437e770b73417a2ecbfc9caf6b771af60d5" + integrity sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg== dependencies: - bignumber.js "^9.0.1" - nofilter "^1.0.4" + nofilter "^3.1.0" chai@^4.2.0: version "4.3.4" @@ -3993,6 +4463,14 @@ chai@^4.2.0: pathval "^1.1.1" type-detect "^4.0.5" +chalk@4.1.2, chalk@^4.1.0, chalk@^4.1.1: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" @@ -4013,14 +4491,6 @@ chalk@^2.0.0, chalk@^2.3.1, chalk@^2.3.2, chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.1.1: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -4113,6 +4583,28 @@ chokidar@^3.4.0: optionalDependencies: fsevents "~2.3.2" +chokidar@^3.5.3: + version "3.6.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chokidar@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.1.tgz#4a6dff66798fb0f72a94f616abbd7e1a19f31d41" + integrity sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA== + dependencies: + readdirp "^4.0.1" + chownr@^1.1.1, chownr@^1.1.2, chownr@^1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" @@ -4167,6 +4659,16 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +clean-stack@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" + integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== + +cli-boxes@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" + integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== + cli-cursor@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" @@ -4184,6 +4686,15 @@ cli-table3@^0.5.0: optionalDependencies: colors "^1.1.2" +cli-table3@^0.6.3: + version "0.6.5" + resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.6.5.tgz#013b91351762739c16a9567c21a04632e449bf2f" + integrity sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ== + dependencies: + string-width "^4.2.0" + optionalDependencies: + "@colors/colors" "1.5.0" + cli-width@^2.0.0: version "2.2.1" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" @@ -4207,6 +4718,15 @@ cliui@^5.0.0: strip-ansi "^5.2.0" wrap-ansi "^5.1.0" +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + clone-buffer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" @@ -4294,7 +4814,7 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -colors@1.4.0, colors@^1.1.2: +colors@^1.1.2: version "1.4.0" resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== @@ -4334,6 +4854,11 @@ commander@^2.15.0, commander@^2.8.1, commander@^2.9.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +commander@^8.1.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" + integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== + compare-func@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-2.0.0.tgz#fb65e75edbddfd2e568554e8b5b05fff7a51fcb3" @@ -4656,7 +5181,7 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.3: +cross-spawn@^7.0.0, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -4782,6 +5307,13 @@ debug@^3.1.0: dependencies: ms "^2.1.1" +debug@^4.3.5: + version "4.3.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" + integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== + dependencies: + ms "^2.1.3" + debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" @@ -4800,6 +5332,11 @@ decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= +decamelize@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" + integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== + decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" @@ -4926,14 +5463,6 @@ deferred-leveldown@~4.0.0: abstract-leveldown "~5.0.0" inherits "^2.0.3" -deferred-leveldown@~5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-5.3.0.tgz#27a997ad95408b61161aa69bd489b86c71b78058" - integrity sha512-a59VOT+oDy7vtAbLRCZwWgxu2BaCfd5Hk7wxJd48ei7I+nsg8Orlb9CLG0PMZienk9BSUKgeAqkO2+Lw+1+Ukw== - dependencies: - abstract-leveldown "~6.2.1" - inherits "^2.0.3" - define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" @@ -5054,6 +5583,11 @@ diff@3.5.0, diff@^3.5.0: resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== +diff@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531" + integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A== + diffie-hellman@^5.0.0: version "5.0.3" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" @@ -5063,6 +5597,13 @@ diffie-hellman@^5.0.0: miller-rabin "^4.0.0" randombytes "^2.0.0" +difflib@^0.2.4: + version "0.2.4" + resolved "https://registry.yarnpkg.com/difflib/-/difflib-0.2.4.tgz#b5e30361a6db023176d562892db85940a718f47e" + integrity sha512-9YVwmMb0wQHQNr5J9m6BSj6fk4pfGITGQOOs+D9Fl+INODWFOfvhIU1hNv6GgR1RBoC/9NJcwu77zShxV0kT7w== + dependencies: + heap ">= 0.2.0" + dir-glob@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" @@ -5189,6 +5730,11 @@ duplexify@^3.4.2, duplexify@^3.6.0: readable-stream "^2.0.0" stream-shift "^1.0.0" +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -5247,6 +5793,11 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -5263,16 +5814,6 @@ encoding-down@5.0.4, encoding-down@~5.0.0: level-errors "^2.0.0" xtend "^4.0.1" -encoding-down@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/encoding-down/-/encoding-down-6.3.0.tgz#b1c4eb0e1728c146ecaef8e32963c549e76d082b" - integrity sha512-QKrV0iKR6MZVJV08QY0wp1e7vF6QbhnbQhb07bwpEyuz4uZiZgPlEGdkCROuFkUwdxlFaiPIhjyarH1ee/3vhw== - dependencies: - abstract-leveldown "^6.2.1" - inherits "^2.0.3" - level-codec "^9.0.0" - level-errors "^2.0.0" - encoding@^0.1.11, encoding@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" @@ -5426,6 +5967,11 @@ escape-string-regexp@1.0.5, escape-string-regexp@^1.0.2, escape-string-regexp@^1 resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + escodegen@1.8.x: version "1.8.1" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" @@ -5517,27 +6063,6 @@ eth-gas-reporter@^0.2.18: sha1 "^1.1.1" sync-request "^6.0.0" -eth-gas-reporter@^0.2.24: - version "0.2.24" - resolved "https://registry.yarnpkg.com/eth-gas-reporter/-/eth-gas-reporter-0.2.24.tgz#768721fec7de02b566e4ebfd123466d275d7035c" - integrity sha512-RbXLC2bnuPHzIMU/rnLXXlb6oiHEEKu7rq2UrAX/0mfo0Lzrr/kb9QTjWjfz8eNvc+uu6J8AuBwI++b+MLNI2w== - dependencies: - "@ethersproject/abi" "^5.0.0-beta.146" - "@solidity-parser/parser" "^0.14.0" - cli-table3 "^0.5.0" - colors "1.4.0" - ethereumjs-util "6.2.0" - ethers "^4.0.40" - fs-readdir-recursive "^1.1.0" - lodash "^4.17.14" - markdown-table "^1.1.3" - mocha "^7.1.1" - req-cwd "^2.0.0" - request "^2.88.0" - request-promise-native "^1.0.5" - sha1 "^1.1.1" - sync-request "^6.0.0" - eth-json-rpc-errors@^1.0.1: version "1.1.1" resolved "https://registry.yarnpkg.com/eth-json-rpc-errors/-/eth-json-rpc-errors-1.1.1.tgz#148377ef55155585981c21ff574a8937f9d6991f" @@ -5709,7 +6234,7 @@ ethereum-common@^0.0.18: resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.0.18.tgz#2fdc3576f232903358976eb39da783213ff9523f" integrity sha1-L9w1dvIykDNYl26znaeDIT/5Uj8= -ethereum-cryptography@^0.1.2, ethereum-cryptography@^0.1.3: +ethereum-cryptography@0.1.3, ethereum-cryptography@^0.1.2, ethereum-cryptography@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" integrity sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ== @@ -5730,6 +6255,26 @@ ethereum-cryptography@^0.1.2, ethereum-cryptography@^0.1.3: secp256k1 "^4.0.1" setimmediate "^1.0.5" +ethereum-cryptography@^1.0.3: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz#5ccfa183e85fdaf9f9b299a79430c044268c9b3a" + integrity sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw== + dependencies: + "@noble/hashes" "1.2.0" + "@noble/secp256k1" "1.7.1" + "@scure/bip32" "1.1.5" + "@scure/bip39" "1.1.1" + +ethereum-cryptography@^2.0.0, ethereum-cryptography@^2.1.2, ethereum-cryptography@^2.1.3: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-2.2.1.tgz#58f2810f8e020aecb97de8c8c76147600b0b8ccf" + integrity sha512-r/W8lkHSiTLxUxW8Rf3u4HGB0xQweG2RyETjywylKZSzLWoWAijRz8WCuOtJ6wah+avllXBqZuk29HCCvhEIRg== + dependencies: + "@noble/curves" "1.4.2" + "@noble/hashes" "1.4.0" + "@scure/bip32" "1.4.0" + "@scure/bip39" "1.3.0" + ethereum-ens@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/ethereum-ens/-/ethereum-ens-0.8.0.tgz#6d0f79acaa61fdbc87d2821779c4e550243d4c57" @@ -5865,7 +6410,7 @@ ethereumjs-util@6.2.0: rlp "^2.2.3" secp256k1 "^3.0.1" -ethereumjs-util@6.2.1, ethereumjs-util@^6.0.0, ethereumjs-util@^6.1.0, ethereumjs-util@^6.2.0: +ethereumjs-util@6.2.1, ethereumjs-util@^6.0.0, ethereumjs-util@^6.1.0, ethereumjs-util@^6.2.0, ethereumjs-util@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69" integrity sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw== @@ -6066,7 +6611,7 @@ ethjs-unit@0.1.6: bn.js "4.11.6" number-to-bn "1.7.0" -ethjs-util@0.1.6, ethjs-util@^0.1.3: +ethjs-util@0.1.6, ethjs-util@^0.1.3, ethjs-util@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/ethjs-util/-/ethjs-util-0.1.6.tgz#f308b62f185f9fe6237132fb2a9818866a5cd536" integrity sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w== @@ -6305,7 +6850,7 @@ fast-deep-equal@^1.0.0: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" integrity sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ= -fast-deep-equal@^3.1.1: +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== @@ -6353,6 +6898,11 @@ fast-safe-stringify@^2.0.6: resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884" integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== +fast-uri@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/fast-uri/-/fast-uri-3.0.3.tgz#892a1c91802d5d7860de728f18608a0573142241" + integrity sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw== + fastq@^1.6.0: version "1.13.0" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" @@ -6511,6 +7061,14 @@ find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + find-yarn-workspace-root@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-1.2.1.tgz#40eb8e6e7c2502ddfaa2577c176f221422f860db" @@ -6533,6 +7091,11 @@ flat@^4.1.0: dependencies: is-buffer "~2.0.3" +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + flow-stoplight@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/flow-stoplight/-/flow-stoplight-1.0.0.tgz#4a292c5bcff8b39fa6cc0cb1a853d86f27eeff7b" @@ -6551,6 +7114,11 @@ follow-redirects@^1.12.1: resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.4.tgz#838fdf48a8bbdd79e52ee51fb1c94e3ed98b9379" integrity sha512-zwGkiSXC1MUJG/qmeIFH2HBJx9u0V46QGUe3YR1fXG8bXQxq7fLj0RjLZQ5nubr9qNJUZrH+xUcwXEoXNpfS+g== +follow-redirects@^1.15.6: + version "1.15.9" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" + integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== + for-each@^0.3.3, for-each@~0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -6575,6 +7143,14 @@ foreach@^2.0.5: resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= +foreground-child@^3.1.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.0.tgz#0ac8644c06e431439f8561db8ecf29a7b5519c77" + integrity sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -6818,7 +7394,7 @@ get-caller-file@^1.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== -get-caller-file@^2.0.1: +get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -7059,6 +7635,30 @@ glob@7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" +glob@7.2.0, glob@^7.0.0, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^10.3.10: + version "10.4.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" + integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + glob@^5.0.15: version "5.0.15" resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" @@ -7070,17 +7670,16 @@ glob@^5.0.15: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.0, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4: - version "7.2.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" - integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== +glob@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" - minimatch "^3.0.4" + minimatch "^5.0.1" once "^1.3.0" - path-is-absolute "^1.0.0" glob@~7.1.7: version "7.1.7" @@ -7276,66 +7875,75 @@ hardhat-abi-exporter@^2.8.0: "@ethersproject/abi" "^5.5.0" delete-empty "^3.0.0" -hardhat-gas-reporter@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/hardhat-gas-reporter/-/hardhat-gas-reporter-1.0.8.tgz#93ce271358cd748d9c4185dbb9d1d5525ec145e0" - integrity sha512-1G5thPnnhcwLHsFnl759f2tgElvuwdkzxlI65fC9PwxYMEe9cmjkVAAWTf3/3y8uP6ZSPiUiOW8PgZnykmZe0g== - dependencies: - array-uniq "1.0.3" - eth-gas-reporter "^0.2.24" +hardhat-gas-reporter@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/hardhat-gas-reporter/-/hardhat-gas-reporter-2.2.1.tgz#ce0b5437fdcaf919479d7a51cf45d8d72cbcd16b" + integrity sha512-3AfPDGBn6VPmRKU65W28qVvG5x+qYL2gH9PAivd31oGj/ZHpZTutqXh6wq46993Vz35rnPDnrGo028U4/NP/Vw== + dependencies: + "@ethersproject/abi" "^5.7.0" + "@ethersproject/bytes" "^5.7.0" + "@ethersproject/units" "^5.7.0" + "@solidity-parser/parser" "^0.18.0" + axios "^1.6.7" + brotli-wasm "^2.0.1" + chalk "4.1.2" + cli-table3 "^0.6.3" + ethereum-cryptography "^2.1.3" + glob "^10.3.10" + jsonschema "^1.4.1" + lodash "^4.17.21" + markdown-table "2.0.0" sha1 "^1.1.1" + viem "2.7.14" -hardhat@^2.6.5: - version "2.6.6" - resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.6.6.tgz#11d2dc4c1659fcbb8632de9399b1b4999f8b3628" - integrity sha512-GneZAwvNxf3cNobYTmMSp2qGX/yJyUmHD/xjW+8zF2AgWkLS33CHmGn4aRFKqfjsk7BS7FU6jqMmmZhHHO3gUw== +hardhat@^2.22.2: + version "2.22.13" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.22.13.tgz#1d2c7c4b640d060ae0f5b04757322118a003955a" + integrity sha512-psVJX4FSXDpSXwsU8OcKTJN04pQEj9cFBMX5OPko+OFwbIoiOpvRmafa954/UaA1934npTj8sV3gaTSdx9bPbA== dependencies: - "@ethereumjs/block" "^3.4.0" - "@ethereumjs/blockchain" "^5.4.0" - "@ethereumjs/common" "^2.4.0" - "@ethereumjs/tx" "^3.3.0" - "@ethereumjs/vm" "^5.5.2" "@ethersproject/abi" "^5.1.2" + "@metamask/eth-sig-util" "^4.0.0" + "@nomicfoundation/edr" "^0.6.3" + "@nomicfoundation/ethereumjs-common" "4.0.4" + "@nomicfoundation/ethereumjs-tx" "5.0.4" + "@nomicfoundation/ethereumjs-util" "9.0.4" + "@nomicfoundation/solidity-analyzer" "^0.1.0" "@sentry/node" "^5.18.1" - "@solidity-parser/parser" "^0.11.0" "@types/bn.js" "^5.1.0" "@types/lru-cache" "^5.1.0" - abort-controller "^3.0.0" adm-zip "^0.4.16" + aggregate-error "^3.0.0" ansi-escapes "^4.3.0" + boxen "^5.1.2" chalk "^2.4.2" - chokidar "^3.4.0" + chokidar "^4.0.0" ci-info "^2.0.0" debug "^4.1.1" enquirer "^2.3.0" env-paths "^2.2.0" - eth-sig-util "^2.5.2" - ethereum-cryptography "^0.1.2" + ethereum-cryptography "^1.0.3" ethereumjs-abi "^0.6.8" - ethereumjs-util "^7.1.0" find-up "^2.1.0" fp-ts "1.19.3" fs-extra "^7.0.1" - glob "^7.1.3" - https-proxy-agent "^5.0.0" + glob "7.2.0" immutable "^4.0.0-rc.12" io-ts "1.10.4" + json-stream-stringify "^3.1.4" + keccak "^3.0.2" lodash "^4.17.11" - merkle-patricia-tree "^4.2.0" mnemonist "^0.38.0" - mocha "^7.1.2" - node-fetch "^2.6.0" - qs "^6.7.0" + mocha "^10.0.0" + p-map "^4.0.0" raw-body "^2.4.1" resolve "1.17.0" semver "^6.3.0" - slash "^3.0.0" - solc "0.7.3" + solc "0.8.26" source-map-support "^0.5.13" stacktrace-parser "^0.1.10" - "true-case-path" "^2.2.1" tsort "0.0.1" - uuid "^3.3.2" + undici "^5.14.0" + uuid "^8.3.2" ws "^7.4.6" has-ansi@^2.0.0: @@ -7467,7 +8075,7 @@ he@1.1.1: resolved "https://registry.yarnpkg.com/he/-/he-1.1.1.tgz#93410fd21b009735151f8868c2f271f3427e23fd" integrity sha1-k0EP0hsAlzUVH4howvJx80J+I/0= -he@1.2.0, he@^1.1.1: +he@1.2.0, he@^1.1.1, he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== @@ -7477,6 +8085,11 @@ heap@0.2.6: resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.6.tgz#087e1f10b046932fc8594dd9e6d378afc9d1e5ac" integrity sha1-CH4fELBGky/IWU3Z5tN4r8nR5aw= +"heap@>= 0.2.0": + version "0.2.7" + resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.7.tgz#1e6adf711d3f27ce35a81fe3b7bd576c2260a8fc" + integrity sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg== + highlight.js@^10.4.1: version "10.7.3" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" @@ -8479,6 +9092,11 @@ is-unc-path@^1.0.0: dependencies: unc-path-regex "^0.1.2" +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + is-utf8@^0.2.0, is-utf8@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" @@ -8558,6 +9176,11 @@ isobject@^3.0.0, isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= +isows@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/isows/-/isows-1.0.3.tgz#93c1cf0575daf56e7120bab5c8c448b0809d0d74" + integrity sha512-2cKei4vlmg2cxEjm3wVSqn8pcoRF/LX/wpifuuNquFO4SQmPwarClT+SUCA2lt+l581tTeZIPIZuIDo2jWN1fg== + isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -8652,6 +9275,15 @@ it-to-stream@^1.0.0: p-fifo "^1.0.0" readable-stream "^3.6.0" +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + js-sha3@0.5.7, js-sha3@^0.5.7: version "0.5.7" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" @@ -8693,6 +9325,13 @@ js-yaml@3.x, js-yaml@^3.13.1: argparse "^1.0.7" esprima "^4.0.0" +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + jsbn@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" @@ -8770,6 +9409,11 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" @@ -8787,6 +9431,11 @@ json-stable-stringify@^1.0.1: dependencies: jsonify "~0.0.0" +json-stream-stringify@^3.1.4: + version "3.1.6" + resolved "https://registry.yarnpkg.com/json-stream-stringify/-/json-stream-stringify-3.1.6.tgz#ebe32193876fb99d4ec9f612389a8d8e2b5d54d4" + integrity sha512-x7fpwxOkbhFCaJDJ8vb1fBY3DdSa4AlITaz+HHILQJzdPMnHEFjxPwVUi1ALIbcIxDE0PNe/0i7frnY8QnBQog== + json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" @@ -8840,6 +9489,11 @@ jsonschema@^1.2.4: resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.4.0.tgz#1afa34c4bc22190d8e42271ec17ac8b3404f87b2" integrity sha512-/YgW6pRMr6M7C+4o8kS+B/2myEpHCrxO4PEWnqJNBFMjn7EWXqlQ4tGwL6xTHeRplwuZmcAncdvfOad1nT2yMw== +jsonschema@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.4.1.tgz#cc4c3f0077fb4542982973d8a083b6b34f482dab" + integrity sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ== + jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" @@ -8877,6 +9531,15 @@ keccak@^3.0.0: node-gyp-build "^4.2.0" readable-stream "^3.6.0" +keccak@^3.0.2: + version "3.0.4" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.4.tgz#edc09b89e633c0549da444432ecf062ffadee86d" + integrity sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q== + dependencies: + node-addon-api "^2.0.0" + node-gyp-build "^4.2.0" + readable-stream "^3.6.0" + keyv@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.0.0.tgz#44923ba39e68b12a7cec7df6c3268c031f2ef373" @@ -9007,11 +9670,6 @@ level-codec@~7.0.0: resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-7.0.1.tgz#341f22f907ce0f16763f24bddd681e395a0fb8a7" integrity sha512-Ua/R9B9r3RasXdRmOtd+t9TCOEIIlts+TN/7XTT2unhDaL6sJn83S3rUyljbr6lVtw49N3/yA0HHjpV6Kzb2aQ== -level-concat-iterator@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/level-concat-iterator/-/level-concat-iterator-2.0.1.tgz#1d1009cf108340252cb38c51f9727311193e6263" - integrity sha512-OTKKOqeav2QWcERMJR7IS9CUo1sHnke2C0gkSmcR7QuEtFNLLzHQAvnMw8ykvEcv0Qtkg0p7FOwP1v9e5Smdcw== - level-errors@^1.0.3: version "1.1.2" resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-1.1.2.tgz#4399c2f3d3ab87d0625f7e3676e2d807deff404d" @@ -9061,15 +9719,6 @@ level-iterator-stream@~3.0.0: readable-stream "^2.3.6" xtend "^4.0.0" -level-iterator-stream@~4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-4.0.2.tgz#7ceba69b713b0d7e22fcc0d1f128ccdc8a24f79c" - integrity sha512-ZSthfEqzGSOMWoUGhTXdX9jv26d32XJuHz/5YnuHZzH6wldfWMOVwI9TBtKcya4BKTyTt3XVA0A3cF3q5CY30Q== - dependencies: - inherits "^2.0.4" - readable-stream "^3.4.0" - xtend "^4.0.2" - level-mem@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/level-mem/-/level-mem-3.0.1.tgz#7ce8cf256eac40f716eb6489654726247f5a89e5" @@ -9078,22 +9727,6 @@ level-mem@^3.0.1: level-packager "~4.0.0" memdown "~3.0.0" -level-mem@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/level-mem/-/level-mem-5.0.1.tgz#c345126b74f5b8aa376dc77d36813a177ef8251d" - integrity sha512-qd+qUJHXsGSFoHTziptAKXoLX87QjR7v2KMbqncDXPxQuCdsQlzmyX+gwrEHhlzn08vkf8TyipYyMmiC6Gobzg== - dependencies: - level-packager "^5.0.3" - memdown "^5.0.0" - -level-packager@^5.0.3: - version "5.1.1" - resolved "https://registry.yarnpkg.com/level-packager/-/level-packager-5.1.1.tgz#323ec842d6babe7336f70299c14df2e329c18939" - integrity sha512-HMwMaQPlTC1IlcwT3+swhqf/NUO+ZhXVz6TY1zZIIZlIR0YSn8GtAAWmIvKjNY16ZkEg/JcpAuQskxsXqC0yOQ== - dependencies: - encoding-down "^6.3.0" - levelup "^4.3.2" - level-packager@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/level-packager/-/level-packager-4.0.1.tgz#7e7d3016af005be0869bc5fa8de93d2a7f56ffe6" @@ -9125,13 +9758,6 @@ level-sublevel@6.6.4: typewiselite "~1.0.0" xtend "~4.0.0" -level-supports@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/level-supports/-/level-supports-1.0.1.tgz#2f530a596834c7301622521988e2c36bb77d122d" - integrity sha512-rXM7GYnW8gsl1vedTJIbzOrRv85c/2uCMpiiCzO2fndd06U/kUXEEU9evYn4zFggBOg36IsBW8LzqIpETwwQzg== - dependencies: - xtend "^4.0.2" - level-ws@0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-0.0.0.tgz#372e512177924a00424b0b43aef2bb42496d228b" @@ -9149,15 +9775,6 @@ level-ws@^1.0.0: readable-stream "^2.2.8" xtend "^4.0.1" -level-ws@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-2.0.0.tgz#207a07bcd0164a0ec5d62c304b4615c54436d339" - integrity sha512-1iv7VXx0G9ec1isqQZ7y5LmoZo/ewAsyDHNA8EFDW5hqH2Kqovm33nSFkSdnLLAK+I5FlT+lo5Cw9itGe+CpQA== - dependencies: - inherits "^2.0.3" - readable-stream "^3.1.0" - xtend "^4.0.1" - levelup@3.1.1, levelup@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/levelup/-/levelup-3.1.1.tgz#c2c0b3be2b4dc316647c53b42e2f559e232d2189" @@ -9181,17 +9798,6 @@ levelup@^1.2.1: semver "~5.4.1" xtend "~4.0.0" -levelup@^4.3.2: - version "4.4.0" - resolved "https://registry.yarnpkg.com/levelup/-/levelup-4.4.0.tgz#f89da3a228c38deb49c48f88a70fb71f01cafed6" - integrity sha512-94++VFO3qN95cM/d6eBXvd894oJE0w3cInq9USsyQzzoJxmiYzPAocNcuGCPGGjoXqDVJcr3C1jzt1TSjyaiLQ== - dependencies: - deferred-leveldown "~5.3.0" - level-errors "~2.0.0" - level-iterator-stream "~4.0.0" - level-supports "~1.0.0" - xtend "~4.0.0" - levn@~0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" @@ -9260,6 +9866,13 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + lodash._reinterpolate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" @@ -9335,6 +9948,11 @@ lodash.templatesettings@^4.0.0: dependencies: lodash._reinterpolate "^3.0.0" +lodash.truncate@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" + integrity sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw== + lodash.uniq@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" @@ -9362,6 +9980,14 @@ log-symbols@3.0.0: dependencies: chalk "^2.4.2" +log-symbols@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + long@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" @@ -9414,6 +10040,11 @@ lru-cache@5.1.1, lru-cache@^5.1.1: dependencies: yallist "^3.0.2" +lru-cache@^10.2.0: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + lru-cache@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-3.2.0.tgz#71789b3b7f5399bec8565dda38aa30d2a097efee" @@ -9522,6 +10153,13 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" +markdown-table@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-2.0.0.tgz#194a90ced26d31fe753d8b9434430214c011865b" + integrity sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A== + dependencies: + repeat-string "^1.0.0" + markdown-table@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-1.1.3.tgz#9fcb69bcfdb8717bfd0398c6ec2d93036ef8de60" @@ -9532,11 +10170,6 @@ math-random@^1.0.1: resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c" integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A== -mcl-wasm@^0.7.1: - version "0.7.9" - resolved "https://registry.yarnpkg.com/mcl-wasm/-/mcl-wasm-0.7.9.tgz#c1588ce90042a8700c3b60e40efb339fc07ab87f" - integrity sha512-iJIUcQWA88IJB/5L15GnJVnSQJmf/YaxxV6zRavv83HILHaJQb6y0iFyDMdDO0gN8X37tdxmAOrH/P8B6RB8sQ== - md5.js@^1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" @@ -9579,18 +10212,6 @@ memdown@^1.0.0: ltgt "~2.2.0" safe-buffer "~5.1.1" -memdown@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/memdown/-/memdown-5.1.0.tgz#608e91a9f10f37f5b5fe767667a8674129a833cb" - integrity sha512-B3J+UizMRAlEArDjWHTMmadet+UKwHd3UjMgGBkZcKAxAYVPS9o0Yeiha4qvz7iGiL2Sb3igUft6p7nbFWctpw== - dependencies: - abstract-leveldown "~6.2.1" - functional-red-black-tree "~1.0.1" - immediate "~3.2.3" - inherits "~2.0.1" - ltgt "~2.2.0" - safe-buffer "~5.2.0" - memdown@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/memdown/-/memdown-3.0.0.tgz#93aca055d743b20efc37492e9e399784f2958309" @@ -9705,24 +10326,16 @@ merkle-patricia-tree@^2.1.2, merkle-patricia-tree@^2.3.2: rlp "^2.0.0" semaphore ">=1.0.1" -merkle-patricia-tree@^4.2.0, merkle-patricia-tree@^4.2.1: - version "4.2.2" - resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-4.2.2.tgz#6dec17855370172458244c2f42c989dd60b773a3" - integrity sha512-eqZYNTshcYx9aESkSPr71EqwsR/QmpnObDEV4iLxkt/x/IoLYZYjJvKY72voP/27Vy61iMOrfOG6jrn7ttXD+Q== - dependencies: - "@types/levelup" "^4.3.0" - ethereumjs-util "^7.1.2" - level-mem "^5.0.1" - level-ws "^2.0.0" - readable-stream "^3.6.0" - rlp "^2.2.4" - semaphore-async-await "^1.5.1" - methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= +micro-ftch@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/micro-ftch/-/micro-ftch-0.3.1.tgz#6cb83388de4c1f279a034fb0cf96dfc050853c5f" + integrity sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg== + micromatch@^2.1.5: version "2.3.11" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" @@ -9846,6 +10459,20 @@ minimalistic-crypto-utils@^1.0.1: dependencies: brace-expansion "^1.1.7" +minimatch@^5.0.1, minimatch@^5.1.6: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + minimist-options@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619" @@ -9881,6 +10508,11 @@ minipass@^2.3.5, minipass@^2.6.0, minipass@^2.9.0: safe-buffer "^5.1.2" yallist "^3.0.0" +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + minizlib@^1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" @@ -9945,6 +10577,32 @@ mnemonist@^0.38.0: dependencies: obliterator "^1.6.1" +mocha@^10.0.0, mocha@^10.2.0: + version "10.7.3" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.7.3.tgz#ae32003cabbd52b59aece17846056a68eb4b0752" + integrity sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A== + dependencies: + ansi-colors "^4.1.3" + browser-stdout "^1.3.1" + chokidar "^3.5.3" + debug "^4.3.5" + diff "^5.2.0" + escape-string-regexp "^4.0.0" + find-up "^5.0.0" + glob "^8.1.0" + he "^1.2.0" + js-yaml "^4.1.0" + log-symbols "^4.1.0" + minimatch "^5.1.6" + ms "^2.1.3" + serialize-javascript "^6.0.2" + strip-json-comments "^3.1.1" + supports-color "^8.1.1" + workerpool "^6.5.1" + yargs "^16.2.0" + yargs-parser "^20.2.9" + yargs-unparser "^2.0.0" + mocha@^4.0.1: version "4.1.0" resolved "https://registry.yarnpkg.com/mocha/-/mocha-4.1.0.tgz#7d86cfbcf35cb829e2754c32e17355ec05338794" @@ -10028,7 +10686,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.0.0, ms@^2.1.1: +ms@^2.0.0, ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -10310,10 +10968,10 @@ node-releases@^2.0.0: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5" integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA== -nofilter@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-1.0.4.tgz#78d6f4b6a613e7ced8b015cec534625f7667006e" - integrity sha512-N8lidFp+fCz+TD51+haYdbDGrcBWwuHX40F5+z0qkUjMJ5Tp+rdSuAkMJ9N9eoolDlEVTf6u5icM+cNKkKW2mA== +nofilter@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-3.1.0.tgz#c757ba68801d41ff930ba2ec55bab52ca184aa66" + integrity sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g== nopt@3.x: version "3.0.6" @@ -10779,6 +11437,13 @@ p-limit@^2.0.0, p-limit@^2.2.0: dependencies: p-try "^2.0.0" +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + p-locate@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" @@ -10800,6 +11465,13 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + p-map-series@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-map-series/-/p-map-series-1.0.0.tgz#bf98fe575705658a9e1351befb85ae4c1f07bdca" @@ -10812,6 +11484,13 @@ p-map@^2.1.0: resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== +p-map@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" + integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== + dependencies: + aggregate-error "^3.0.0" + p-pipe@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/p-pipe/-/p-pipe-1.2.0.tgz#4b1a11399a11520a67790ee5a0c1d5881d6befe9" @@ -10860,6 +11539,11 @@ p-waterfall@^1.0.0: dependencies: p-reduce "^1.0.0" +package-json-from-dist@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + pako@^1.0.4: version "1.0.11" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" @@ -11066,6 +11750,14 @@ path-parse@^1.0.6: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-starts-with@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/path-starts-with/-/path-starts-with-2.0.0.tgz#ffd6d51926cd497022b44d392196033d5451892f" @@ -11306,6 +11998,11 @@ proxy-addr@~2.0.5: forwarded "0.2.0" ipaddr.js "1.9.1" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" @@ -11627,7 +12324,7 @@ read@1, read@~1.0.1: string_decoder "~1.1.1" util-deprecate "~1.0.1" -"readable-stream@2 || 3", readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.0.6, readable-stream@^3.1.0, readable-stream@^3.4.0, readable-stream@^3.6.0: +"readable-stream@2 || 3", readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.0.6, readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -11675,6 +12372,11 @@ readdirp@^2.0.0: micromatch "^3.1.10" readable-stream "^2.0.2" +readdirp@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.0.2.tgz#388fccb8b75665da3abffe2d8f8ed59fe74c230a" + integrity sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA== + readdirp@~3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.2.0.tgz#c30c33352b12c96dfb4b895421a49fd5a9593839" @@ -11829,7 +12531,7 @@ repeat-element@^1.1.2: resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== -repeat-string@^1.5.2, repeat-string@^1.6.1: +repeat-string@^1.0.0, repeat-string@^1.5.2, repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= @@ -11907,7 +12609,7 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= -require-from-string@^2.0.0: +require-from-string@^2.0.0, require-from-string@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== @@ -12172,11 +12874,6 @@ seek-bzip@^1.0.5: dependencies: commander "^2.8.1" -semaphore-async-await@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/semaphore-async-await/-/semaphore-async-await-1.5.1.tgz#857bef5e3644601ca4b9570b87e9df5ca12974fa" - integrity sha1-hXvvXjZEYBykuVcLh+nfXKEpdPo= - semaphore@>=1.0.1, semaphore@^1.0.3, semaphore@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/semaphore/-/semaphore-1.1.0.tgz#aaad8b86b20fe8e9b32b16dc2ee682a8cd26a8aa" @@ -12228,6 +12925,13 @@ send@0.17.1: range-parser "~1.2.1" statuses "~1.5.0" +serialize-javascript@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== + dependencies: + randombytes "^2.1.0" + serve-static@1.14.1: version "1.14.1" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" @@ -12354,6 +13058,11 @@ signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.5.tgz#9e3e8cc0c75a99472b44321033a7702e7738252f" integrity sha512-KWcOiKeQj6ZyXx7zq4YxSMgHRlod4czeBQZrPb8OKcohcqAXShm7E20kEMle9WBt26hFcAf0qLOcp5zmY7kOqQ== +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + simple-concat@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" @@ -12383,6 +13092,15 @@ slash@^3.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== + dependencies: + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" + slide@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/slide/-/slide-1.1.6.tgz#56eb027d65b4d2dce6cb2e2d32c4d4afc9e1d707" @@ -12463,18 +13181,16 @@ solc@0.6.8: semver "^5.5.0" tmp "0.0.33" -solc@0.7.3: - version "0.7.3" - resolved "https://registry.yarnpkg.com/solc/-/solc-0.7.3.tgz#04646961bd867a744f63d2b4e3c0701ffdc7d78a" - integrity sha512-GAsWNAjGzIDg7VxzP6mPjdurby3IkGCjQcM8GFYZT6RyaoUZKmMU6Y7YwG+tFGhv7dwZ8rmR4iwFDrrD99JwqA== +solc@0.8.26: + version "0.8.26" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.8.26.tgz#afc78078953f6ab3e727c338a2fefcd80dd5b01a" + integrity sha512-yiPQNVf5rBFHwN6SIf3TUUvVAFKcQqmSUFeq+fb6pNRCo0ZCgpYOZDi3BVoezCPIAcKrVYd/qXlBLUP9wVrZ9g== dependencies: command-exists "^1.2.8" - commander "3.0.2" + commander "^8.1.0" follow-redirects "^1.12.1" - fs-extra "^0.30.0" js-sha3 "0.8.0" memorystream "^0.3.1" - require-from-string "^2.0.0" semver "^5.5.0" tmp "0.0.33" @@ -12503,6 +13219,31 @@ solidity-coverage@^0.7.9: shelljs "^0.8.3" web3-utils "^1.3.0" +solidity-coverage@^0.8.0: + version "0.8.13" + resolved "https://registry.yarnpkg.com/solidity-coverage/-/solidity-coverage-0.8.13.tgz#8eeada2e82ae19d25568368aa782a2baad0e0ce7" + integrity sha512-RiBoI+kF94V3Rv0+iwOj3HQVSqNzA9qm/qDP1ZDXK5IX0Cvho1qiz8hAXTsAo6KOIUeP73jfscq0KlLqVxzGWA== + dependencies: + "@ethersproject/abi" "^5.0.9" + "@solidity-parser/parser" "^0.18.0" + chalk "^2.4.2" + death "^1.1.0" + difflib "^0.2.4" + fs-extra "^8.1.0" + ghost-testrpc "^0.0.2" + global-modules "^2.0.0" + globby "^10.0.1" + jsonschema "^1.2.4" + lodash "^4.17.21" + mocha "^10.2.0" + node-emoji "^1.10.0" + pify "^4.0.1" + recursive-readdir "^2.2.2" + sc-istanbul "^0.4.5" + semver "^7.3.4" + shelljs "^0.8.3" + web3-utils "^1.3.6" + solium-plugin-security@0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/solium-plugin-security/-/solium-plugin-security-0.1.1.tgz#2a87bcf8f8c3abf7d198e292e4ac080284e3f3f6" @@ -12767,6 +13508,15 @@ strict-uri-encode@^2.0.0: resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546" integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY= +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -12784,7 +13534,7 @@ string-width@^1.0.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" -"string-width@^1.0.2 || 2 || 3 || 4": +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -12802,6 +13552,15 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + string.prototype.trim@~1.2.4: version "1.2.5" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.5.tgz#a587bcc8bfad8cb9829a577f5de30dd170c1682c" @@ -12846,6 +13605,13 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^3.0.0, strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" @@ -12867,13 +13633,20 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.1: +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + strip-bom@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" @@ -12934,6 +13707,11 @@ strip-json-comments@2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + strip-outer@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/strip-outer/-/strip-outer-1.0.1.tgz#b2fd2abf6604b9d1e6013057195df836b8a9d631" @@ -12995,6 +13773,13 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +supports-color@^8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + swarm-js@^0.1.40: version "0.1.40" resolved "https://registry.yarnpkg.com/swarm-js/-/swarm-js-0.1.40.tgz#b1bc7b6dcc76061f6c772203e004c11997e06b99" @@ -13028,6 +13813,17 @@ sync-rpc@^1.2.1: dependencies: get-port "^3.1.0" +table@^6.8.0: + version "6.8.2" + resolved "https://registry.yarnpkg.com/table/-/table-6.8.2.tgz#c5504ccf201213fa227248bdc8c5569716ac6c58" + integrity sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA== + dependencies: + ajv "^8.0.1" + lodash.truncate "^4.4.2" + slice-ansi "^4.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + tape@^4.6.3: version "4.14.0" resolved "https://registry.yarnpkg.com/tape/-/tape-4.14.0.tgz#e4d46097e129817175b90925f2385f6b1bcfa826" @@ -13315,11 +14111,6 @@ trim-right@^1.0.1: resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= -"true-case-path@^2.2.1": - version "2.2.1" - resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-2.2.1.tgz#c5bf04a5bbec3fd118be4084461b3a27c4d796bf" - integrity sha512-0z3j8R7MCjy10kc/g+qg7Ln3alJTodw9aDuVWZa3uiWqfuBMKeAeP2ocWcxoyM3D73yz3Jt/Pu4qPr4wHSdB/Q== - truffle-flattener@^1.2.9: version "1.5.0" resolved "https://registry.yarnpkg.com/truffle-flattener/-/truffle-flattener-1.5.0.tgz#c358fa3e5cb0a663429f7912a58c4f0879414e64" @@ -13363,7 +14154,7 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" -tweetnacl-util@^0.15.0: +tweetnacl-util@^0.15.0, tweetnacl-util@^0.15.1: version "0.15.1" resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b" integrity sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw== @@ -13395,6 +14186,11 @@ type-fest@^0.18.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + type-fest@^0.21.3: version "0.21.3" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" @@ -13539,6 +14335,13 @@ underscore@^1.8.3: resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.1.tgz#0c1c6bd2df54b6b69f2314066d65b6cde6fcf9d1" integrity sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g== +undici@^5.14.0: + version "5.28.4" + resolved "https://registry.yarnpkg.com/undici/-/undici-5.28.4.tgz#6b280408edb6a1a604a9b20340f45b422e373068" + integrity sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g== + dependencies: + "@fastify/busboy" "^2.0.0" + union-value@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" @@ -13676,7 +14479,7 @@ util-promisify@^2.1.0: dependencies: object.getownpropertydescriptors "^2.0.3" -util.promisify@^1.0.0, util.promisify@^1.0.1: +util.promisify@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.1.1.tgz#77832f57ced2c9478174149cae9b96e9918cd54b" integrity sha512-/s3UsZUrIfa6xDhr7zZhnE9SLQ5RIXyYfiVnMMyMDzOc8WhWN4Nbh36H842OyurKbCDAesZOJaVyvmSl6fhGQw== @@ -13768,6 +14571,20 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +viem@2.7.14: + version "2.7.14" + resolved "https://registry.yarnpkg.com/viem/-/viem-2.7.14.tgz#347d316cb5400f0b896b2205b1bc8073aa5e27e0" + integrity sha512-5b1KB1gXli02GOQHZIUsRluNUwssl2t4hqdFAzyWPwJ744N83jAOBOjOkrGz7K3qMIv9b0GQt3DoZIErSQTPkQ== + dependencies: + "@adraffy/ens-normalize" "1.10.0" + "@noble/curves" "1.2.0" + "@noble/hashes" "1.3.2" + "@scure/bip32" "1.3.2" + "@scure/bip39" "1.2.1" + abitype "1.0.0" + isows "1.0.3" + ws "8.13.0" + vinyl-fs@^3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-3.0.3.tgz#c85849405f67428feabbbd5c5dbdd64f47d31bc7" @@ -14986,6 +15803,20 @@ web3-utils@1.7.1: randombytes "^2.1.0" utf8 "3.0.0" +web3-utils@^1.3.6: + version "1.10.4" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.10.4.tgz#0daee7d6841641655d8b3726baf33b08eda1cbec" + integrity sha512-tsu8FiKJLk2PzhDl9fXbGUWTkkVXYhtTA+SmEFkKft+9BgwLxfCRpU96sWv7ICC8zixBNd3JURVoiR3dUXgP8A== + dependencies: + "@ethereumjs/util" "^8.1.0" + bn.js "^5.2.1" + ethereum-bloom-filters "^1.0.6" + ethereum-cryptography "^2.1.2" + ethjs-unit "0.1.6" + number-to-bn "1.7.0" + randombytes "^2.1.0" + utf8 "3.0.0" + web3@1.2.11: version "1.2.11" resolved "https://registry.yarnpkg.com/web3/-/web3-1.2.11.tgz#50f458b2e8b11aa37302071c170ed61cff332975" @@ -15163,6 +15994,13 @@ wide-align@^1.1.0: dependencies: string-width "^1.0.2 || 2 || 3 || 4" +widest-line@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" + integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== + dependencies: + string-width "^4.0.0" + windows-release@^3.1.0: version "3.3.3" resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-3.3.3.tgz#1c10027c7225743eec6b89df160d64c2e0293999" @@ -15180,6 +16018,20 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= +workerpool@^6.5.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544" + integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA== + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" @@ -15197,6 +16049,24 @@ wrap-ansi@^5.1.0: string-width "^3.0.0" strip-ansi "^5.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -15248,6 +16118,11 @@ ws@7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.6.tgz#5654ca8ecdeee47c33a9a4bf6d28e2be2980377c" integrity sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A== +ws@8.13.0: + version "8.13.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" + integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== + ws@^3.0.0: version "3.3.3" resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" @@ -15311,7 +16186,7 @@ xmlhttprequest@1.8.0: resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" integrity sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw= -xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.0, xtend@~4.0.1: +xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0, xtend@~4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== @@ -15333,6 +16208,11 @@ y18n@^3.2.1: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + yaeti@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" @@ -15377,7 +16257,7 @@ yargs-parser@^15.0.1: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^20.2.3: +yargs-parser@^20.2.2, yargs-parser@^20.2.3, yargs-parser@^20.2.9: version "20.2.9" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== @@ -15398,6 +16278,16 @@ yargs-unparser@1.6.0: lodash "^4.17.15" yargs "^13.3.0" +yargs-unparser@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" + integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== + dependencies: + camelcase "^6.0.0" + decamelize "^4.0.0" + flat "^5.0.2" + is-plain-obj "^2.1.0" + yargs@13.2.4: version "13.2.4" resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.2.4.tgz#0b562b794016eb9651b98bd37acf364aa5d6dc83" @@ -15484,6 +16374,19 @@ yargs@^14.2.2: y18n "^4.0.0" yargs-parser "^15.0.1" +yargs@^16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + yauzl@^2.4.2: version "2.10.0" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" @@ -15491,3 +16394,8 @@ yauzl@^2.4.2: dependencies: buffer-crc32 "~0.2.3" fd-slicer "~1.1.0" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== From c88466e687173f7380390ee22ce8f4a638de4b8e Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 17 Dec 2024 13:25:45 +0100 Subject: [PATCH 094/100] fix: update node in verify-bytecode action --- .github/workflows/verify-bytecode.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/verify-bytecode.yml b/.github/workflows/verify-bytecode.yml index 0ae732d23..79f448502 100644 --- a/.github/workflows/verify-bytecode.yml +++ b/.github/workflows/verify-bytecode.yml @@ -18,7 +18,7 @@ jobs: - name: Install nodejs uses: actions/setup-node@v3 with: - node-version: 14 + node-version: 18 - name: Install dependencies run: yarn From c54aea49f45742f1071a5b48b4fd95029a1074a2 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 17 Dec 2024 14:18:03 +0100 Subject: [PATCH 095/100] fix: add cache clean step to verify-bytecode action --- .github/workflows/verify-bytecode.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/workflows/verify-bytecode.yml b/.github/workflows/verify-bytecode.yml index 79f448502..e37c70f4a 100644 --- a/.github/workflows/verify-bytecode.yml +++ b/.github/workflows/verify-bytecode.yml @@ -1,4 +1,3 @@ ---- name: Verify deployed contracts' bytecode on: @@ -23,10 +22,16 @@ jobs: - name: Install dependencies run: yarn + # Add cache cleanup step + - name: Clean compiler cache + run: rm -rf ~/.cache/buidler-nodejs/compilers + + # Modify compile step with memory settings - name: Compile contracts - run: > + run: | + export NODE_OPTIONS="--max-old-space-size=4096 --stack-size=3000" jq -r '.scripts?.compile | strings | input_filename' apps/*/package.json | - xargs dirname | xargs -I% sh -c "echo % && cd % && yarn compile" + xargs dirname | xargs -I% sh -c "echo % && cd % && NODE_OPTIONS='--max-old-space-size=4096 --stack-size=3000' yarn compile" - name: Verify bytecode of contracts uses: lidofinance/action-verify-bytecode@master From 63575366cc5a8df76b09f4ab5cd69ead7e0c6335 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 17 Dec 2024 14:24:15 +0100 Subject: [PATCH 096/100] fix: ci --- .github/workflows/verify-bytecode.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/verify-bytecode.yml b/.github/workflows/verify-bytecode.yml index e37c70f4a..baf731f44 100644 --- a/.github/workflows/verify-bytecode.yml +++ b/.github/workflows/verify-bytecode.yml @@ -22,16 +22,14 @@ jobs: - name: Install dependencies run: yarn - # Add cache cleanup step - name: Clean compiler cache run: rm -rf ~/.cache/buidler-nodejs/compilers - # Modify compile step with memory settings - name: Compile contracts run: | - export NODE_OPTIONS="--max-old-space-size=4096 --stack-size=3000" + export NODE_OPTIONS="--max-old-space-size=4096" jq -r '.scripts?.compile | strings | input_filename' apps/*/package.json | - xargs dirname | xargs -I% sh -c "echo % && cd % && NODE_OPTIONS='--max-old-space-size=4096 --stack-size=3000' yarn compile" + xargs dirname | xargs -I% sh -c "echo % && cd % && NODE_OPTIONS='--max-old-space-size=4096' yarn compile" - name: Verify bytecode of contracts uses: lidofinance/action-verify-bytecode@master From 481f8923ad5a230336f6f9dca43c6093afb8bf4e Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 17 Dec 2024 14:33:21 +0100 Subject: [PATCH 097/100] fix: revert verify-bytecode action code --- .github/workflows/verify-bytecode.yml | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/.github/workflows/verify-bytecode.yml b/.github/workflows/verify-bytecode.yml index baf731f44..0ae732d23 100644 --- a/.github/workflows/verify-bytecode.yml +++ b/.github/workflows/verify-bytecode.yml @@ -1,3 +1,4 @@ +--- name: Verify deployed contracts' bytecode on: @@ -17,19 +18,15 @@ jobs: - name: Install nodejs uses: actions/setup-node@v3 with: - node-version: 18 + node-version: 14 - name: Install dependencies run: yarn - - name: Clean compiler cache - run: rm -rf ~/.cache/buidler-nodejs/compilers - - name: Compile contracts - run: | - export NODE_OPTIONS="--max-old-space-size=4096" + run: > jq -r '.scripts?.compile | strings | input_filename' apps/*/package.json | - xargs dirname | xargs -I% sh -c "echo % && cd % && NODE_OPTIONS='--max-old-space-size=4096' yarn compile" + xargs dirname | xargs -I% sh -c "echo % && cd % && yarn compile" - name: Verify bytecode of contracts uses: lidofinance/action-verify-bytecode@master From 2a6c85211e86cc6c35f762da912ea4832d3cf1e9 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 17 Dec 2024 17:22:22 +0100 Subject: [PATCH 098/100] fix: ci --- .github/workflows/verify-bytecode.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/verify-bytecode.yml b/.github/workflows/verify-bytecode.yml index 0ae732d23..0b0ed8ae6 100644 --- a/.github/workflows/verify-bytecode.yml +++ b/.github/workflows/verify-bytecode.yml @@ -18,14 +18,14 @@ jobs: - name: Install nodejs uses: actions/setup-node@v3 with: - node-version: 14 + node-version: 18 - name: Install dependencies run: yarn - name: Compile contracts run: > - jq -r '.scripts?.compile | strings | input_filename' apps/*/package.json | + jq -r '.scripts?.compile | select(contains("hardhat")) | input_filename' apps/*/package.json | xargs dirname | xargs -I% sh -c "echo % && cd % && yarn compile" - name: Verify bytecode of contracts From 3cd99c056c16c2f471044c50f9425b024222452d Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 17 Dec 2024 17:29:32 +0100 Subject: [PATCH 099/100] fix: artifacts --- artifacts.json | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/artifacts.json b/artifacts.json index 065d0089a..2efff0f3b 100644 --- a/artifacts.json +++ b/artifacts.json @@ -1,29 +1,9 @@ [ - { - "artifactPath": "apps/agent/artifacts/Agent.json", - "sourcePath": "apps/agent/contracts/Agent.sol", - "name": "Agent", - "address": "0x3A93C17FC82CC33420d1809dDA9Fb715cc89dd37", - "txHash": "0x20ca936d77abb19e68654b984c32f0022bff6949551fd1e127dca0845d4bf5bd" - }, - { - "artifactPath": "apps/finance/artifacts/Finance.json", - "sourcePath": "apps/finance/contracts/Finance.sol", - "name": "Finance", - "address": "0x836835289A2E81B66AE5d95b7c8dBC0480dCf9da", - "txHash": "0x3866bca78b56a538fbea0d9b8932798646c9605aad540818f2bd53086f4bab09" - }, - { - "artifactPath": "apps/token-manager/artifacts/TokenManager.json", - "sourcePath": "apps/token-manager/contracts/TokenManager.sol", - "name": "TokenManager", - "address": "0xde3A93028F2283cc28756B3674BD657eaFB992f4", - "txHash": "0x774688b324fd1f1868ac969d89c4b59707e4ae0cf837781ef78993d21b3a984e" - }, { "artifactPath": "apps/voting/artifacts/contracts/Voting.sol/Voting.json", "sourcePath": "apps/voting/contracts/Voting.sol", "name": "Voting", - "address": "0x72fb5253AD16307B9E773d2A78CaC58E309d5Ba4" + "address": "0x72fb5253AD16307B9E773d2A78CaC58E309d5Ba4", + "txHash": "0x077645dd83e2af290b1b83c3d77767ac37c4de40e2e813e0a4e4ff37a7755834" } ] From a0595056afac245f05bf4b9248cf50dcaf8a5936 Mon Sep 17 00:00:00 2001 From: Alexander Belokon Date: Tue, 17 Dec 2024 17:31:32 +0100 Subject: [PATCH 100/100] fix: artifacts --- artifacts.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/artifacts.json b/artifacts.json index 2efff0f3b..a76bca39c 100644 --- a/artifacts.json +++ b/artifacts.json @@ -3,7 +3,7 @@ "artifactPath": "apps/voting/artifacts/contracts/Voting.sol/Voting.json", "sourcePath": "apps/voting/contracts/Voting.sol", "name": "Voting", - "address": "0x72fb5253AD16307B9E773d2A78CaC58E309d5Ba4", + "address": "0xf165148978fa3ce74d76043f833463c340cfb704", "txHash": "0x077645dd83e2af290b1b83c3d77767ac37c4de40e2e813e0a4e4ff37a7755834" } ]