Skip to content

Commit bcb7728

Browse files
committed
refactor: clarify auhtorizeUpgrade logic
1 parent 3151616 commit bcb7728

File tree

5 files changed

+48
-14
lines changed

5 files changed

+48
-14
lines changed

src/XanV1.sol

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,9 @@ contract XanV1 is
6464
error ImplementationNotDelayed(address expected, address actual);
6565

6666
error UpgradeNotScheduled(address impl);
67+
error UpgradeScheduledTwice(address impl);
6768
error UpgradeAlreadyScheduled(address impl, uint48 endTime);
6869
error UpgradeCancellationInvalid(address impl, uint48 endTime);
69-
error UpgradeIsNotAllowedToBeScheduledTwice(address impl);
7070

7171
error QuorumOrMinLockedSupplyNotReached(address impl);
7272
error QuorumAndMinLockedSupplyReached(address impl);
@@ -311,6 +311,11 @@ contract XanV1 is
311311
}
312312
}
313313

314+
/// @notice @inheritdoc IXanV1
315+
function proposedImplementationsCount() external view returns (uint48 count) {
316+
count = _getVotingData().implementationsCount();
317+
}
318+
314319
/// @notice @inheritdoc IXanV1
315320
function lockedSupply() public view override returns (uint256 locked) {
316321
locked = _getLockingData().lockedSupply;
@@ -415,13 +420,12 @@ contract XanV1 is
415420
bool isScheduledByVoterBody = (newImpl == votingData.scheduledImpl);
416421
bool isScheduledByCouncil = (newImpl == councilData.scheduledImpl);
417422

418-
// NOTE: councilUpgrade.impl and voterBodyUpgrade.impl can still be address(0);
419-
420-
if (isScheduledByCouncil && isScheduledByVoterBody) {
421-
// This should never happen.
422-
revert UpgradeIsNotAllowedToBeScheduledTwice(newImpl);
423+
if (isScheduledByVoterBody && isScheduledByCouncil) {
424+
// This state should never be reached.
425+
revert UpgradeScheduledTwice(newImpl);
423426
}
424427

428+
// Cache the best ranked implementation proposed by the voter body.
425429
address bestRankedVoterBodyImpl = votingData.implementationByRank(0);
426430

427431
if (isScheduledByVoterBody) {
@@ -438,12 +442,14 @@ contract XanV1 is
438442
}
439443

440444
if (isScheduledByCouncil) {
441-
// Revert if the quorum and minimum locked supply is reached for best ranked implementation proposed by
442-
// the voter body.
443-
if (_isQuorumAndMinLockedSupplyReached(bestRankedVoterBodyImpl)) {
444-
revert QuorumAndMinLockedSupplyReached(bestRankedVoterBodyImpl);
445+
// Check if the best ranked implementation exists.
446+
if (bestRankedVoterBodyImpl != address(0)) {
447+
// Revert if the quorum and minimum locked supply is reached for best ranked implementation proposed by
448+
// the voter body and it could therefore could be scheduled.
449+
if (_isQuorumAndMinLockedSupplyReached(bestRankedVoterBodyImpl)) {
450+
revert QuorumAndMinLockedSupplyReached(bestRankedVoterBodyImpl);
451+
}
445452
}
446-
447453
_checkDelayCriterion({endTime: councilData.scheduledEndTime});
448454

449455
return;

src/interfaces/IXanV1.sol

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@ interface IXanV1 {
129129
/// @return impl The proposed implementation with the respective rank.
130130
function proposedImplementationByRank(uint48 rank) external view returns (address impl);
131131

132+
/// @notice Returns the number of implementations proposed by the voter body.
133+
/// @return count The proposed implementation count.
134+
function proposedImplementationsCount() external view returns (uint48 count);
135+
132136
/// @notice Returns the address of the governance council.
133137
/// @return council The governance council address.
134138
function governanceCouncil() external view returns (address council);

src/libs/Voting.sol

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,4 +126,10 @@ library Voting {
126126

127127
impl = data.ranking[rank];
128128
}
129+
130+
/// @notice Returns the number of implementations.
131+
/// @return count implementation count.
132+
function implementationsCount(Data storage data) internal view returns (uint48 count) {
133+
count = data.implCount;
134+
}
129135
}

test/XanV1.upgrade.t.sol

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,19 @@ contract XanV1UpgradeTest is Test {
4646
_xanProxy.upgradeToAndCall({newImplementation: address(0), data: ""});
4747
}
4848

49-
function test_authorizeUpgrade_reverts_voter_body_upgrade_if_implementation_has_not_been_voted_on() public {
49+
function test_authorizeUpgrade_reverts_voter_body_upgrade_if_no_implementation_has_been_scheduled() public {
50+
vm.expectRevert(
51+
abi.encodeWithSelector(XanV1.UpgradeNotScheduled.selector, _voterProposedImpl), address(_xanProxy)
52+
);
53+
_xanProxy.upgradeToAndCall({newImplementation: _voterProposedImpl, data: ""});
54+
}
55+
56+
function test_authorizeUpgrade_reverts_voter_body_upgrade_if_implementation_has_not_been_scheduled() public {
57+
vm.startPrank(_defaultSender);
58+
_xanProxy.lock(Parameters.MIN_LOCKED_SUPPLY);
59+
_xanProxy.castVote(_voterProposedImpl2);
60+
vm.stopPrank();
61+
5062
vm.expectRevert(
5163
abi.encodeWithSelector(XanV1.UpgradeNotScheduled.selector, _voterProposedImpl), address(_xanProxy)
5264
);

test/XanV1.voting.t.sol

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,9 +55,11 @@ contract XanV1VotingTest is Test {
5555

5656
function test_castVote_ranks_an_implementation_on_first_vote() public {
5757
// Check that no implementation has rank 0.
58+
uint48 count = _xanProxy.proposedImplementationsCount();
5859
uint48 rank = 0;
60+
5961
vm.expectRevert(
60-
abi.encodeWithSelector(XanV1.ImplementationRankNonExistent.selector, 0, rank), address(_xanProxy)
62+
abi.encodeWithSelector(XanV1.ImplementationRankNonExistent.selector, count, rank), address(_xanProxy)
6163
);
6264
_xanProxy.proposedImplementationByRank(rank);
6365

@@ -66,12 +68,16 @@ contract XanV1VotingTest is Test {
6668
_xanProxy.lock(Parameters.MIN_LOCKED_SUPPLY);
6769
_xanProxy.castVote(_NEW_IMPL);
6870
vm.stopPrank();
71+
72+
count = _xanProxy.proposedImplementationsCount();
73+
assertEq(count, 1);
74+
6975
assertEq(_NEW_IMPL, _xanProxy.proposedImplementationByRank(rank));
7076

7177
// Check that no implementation has rank 1.
7278
rank = 1;
7379
vm.expectRevert(
74-
abi.encodeWithSelector(XanV1.ImplementationRankNonExistent.selector, 1, rank), address(_xanProxy)
80+
abi.encodeWithSelector(XanV1.ImplementationRankNonExistent.selector, count, rank), address(_xanProxy)
7581
);
7682
_xanProxy.proposedImplementationByRank(rank);
7783
}

0 commit comments

Comments
 (0)