Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions src/DelegationContract.sol
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,18 @@ contract DelegationContract is IDelegationContract, IERC1271, IERC5313, IERC165

/// @inheritdoc IDelegationContract
function revokeDelegate() external onlyOwner notTerminated {
address revoked = getDelegate();
_settle();

address pending = _pendingDelegate;
if (pending != address(0)) {
_pendingDelegate = address(0);
_pendingActiveFrom = 0;

emit NominationRevoked(pending);
}

address revoked = _currentDelegate;
_currentDelegate = address(0);
_pendingDelegate = address(0);
_pendingActiveFrom = 0;

emit DelegateRevoked(revoked);
}
Expand Down
2 changes: 2 additions & 0 deletions src/interfaces/IDelegationContract.sol
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ interface IDelegationContract {

/// @notice Immediately remove the current and pending delegate.
/// Only callable by owner.
/// Emits NominationRevoked if a not-yet-effective nomination is
/// dropped, in addition to DelegateRevoked.
/// Reverts if the contract is terminated.
function revokeDelegate() external;

Expand Down
47 changes: 46 additions & 1 deletion test/unit/DelegationContract.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

pragma solidity 0.8.35;

import { Test } from "forge-std/Test.sol";
import { Test, Vm } from "forge-std/Test.sol";

import { IERC165 } from "@openzeppelin/contracts/interfaces/IERC165.sol";
import { IERC1271 } from "@openzeppelin/contracts/interfaces/IERC1271.sol";
Expand Down Expand Up @@ -377,6 +377,51 @@ contract DelegationContractRevokeDelegateTest is DelegationContractBaseTestWithD
assertEq(activeFrom, 0);
}

function test_revokeDelegate_emitsNominationRevokedForPending() public {
address newDelegate = nextAddress("NEW_DELEGATE");

vm.prank(owner);
delegationContract.nominateDelegate(newDelegate);

vm.expectEmit();
emit IDelegationContract.NominationRevoked(newDelegate);
vm.expectEmit();
emit IDelegationContract.DelegateRevoked(delegate);

vm.prank(owner);
delegationContract.revokeDelegate();
}

function test_revokeDelegate_noNominationRevokedWhenNoPending() public {
vm.recordLogs();

vm.prank(owner);
delegationContract.revokeDelegate();

Vm.Log[] memory logs = vm.getRecordedLogs();
assertEq(logs.length, 1, "Only DelegateRevoked should be emitted");
assertEq(logs[0].topics[0], IDelegationContract.DelegateRevoked.selector);
}

function test_revokeDelegate_noNominationRevokedWhenPendingMatured() public {
address newDelegate = nextAddress("NEW_DELEGATE");

vm.prank(owner);
delegationContract.nominateDelegate(newDelegate);

vm.warp(block.timestamp + cooldown);

vm.recordLogs();

vm.prank(owner);
delegationContract.revokeDelegate();

Vm.Log[] memory logs = vm.getRecordedLogs();
assertEq(logs.length, 1, "Matured pending is the effective delegate: only DelegateRevoked");
assertEq(logs[0].topics[0], IDelegationContract.DelegateRevoked.selector);
assertEq(address(uint160(uint256(logs[0].topics[1]))), newDelegate);
}

function test_revokeDelegate_noOpWhenNoDelegate() public {
vm.prank(owner);
delegationContract.revokeDelegate();
Expand Down
Loading