diff --git a/src/VotesPartialDelegationUpgradeable.sol b/src/VotesPartialDelegationUpgradeable.sol index 0245912..275fe27 100644 --- a/src/VotesPartialDelegationUpgradeable.sol +++ b/src/VotesPartialDelegationUpgradeable.sol @@ -301,6 +301,14 @@ abstract contract VotesPartialDelegationUpgradeable is _delegate(_delegator, _partialDelegations); } + /** + * @dev Allows an address to increment their nonce and therefore invalidate any pending signed + * actions. + */ + function invalidateNonce() external { + _useNonce(msg.sender); + } + /** * @dev Delegate `_delegator`'s voting units to delegates specified in `_newDelegations`. * Emits events {IVotes-DelegateChanged} and {IVotes-DelegateVotesChanged}. diff --git a/test/ERC20VotesPartialDelegationUpgradeable.t.sol b/test/ERC20VotesPartialDelegationUpgradeable.t.sol index 23b5660..7bed6a4 100644 --- a/test/ERC20VotesPartialDelegationUpgradeable.t.sol +++ b/test/ERC20VotesPartialDelegationUpgradeable.t.sol @@ -1172,6 +1172,41 @@ contract DelegatePartiallyOnBehalf is PartialDelegationTest { } } +contract InvalidateNonce is PartialDelegationTest { + using stdStorage for StdStorage; + + function testFuzz_SucessfullyIncrementsTheNonceOfTheSender(address _caller, uint256 _initialNonce) public { + vm.assume(_caller != address(0)); + vm.assume(_initialNonce != type(uint256).max); + + stdstore.target(address(tokenProxy)).sig("nonces(address)").with_key(_caller).checked_write(_initialNonce); + + vm.prank(_caller); + tokenProxy.invalidateNonce(); + + uint256 currentNonce = tokenProxy.nonces(_caller); + + assertEq(currentNonce, _initialNonce + 1, "Current nonce is incorrect"); + } + + function testFuzz_IncreasesTheNonceByTwoWhenCalledTwice(address _caller, uint256 _initialNonce) public { + vm.assume(_caller != address(0)); + _initialNonce = bound(_initialNonce, 0, type(uint256).max - 2); + + stdstore.target(address(tokenProxy)).sig("nonces(address)").with_key(_caller).checked_write(_initialNonce); + + vm.prank(_caller); + tokenProxy.invalidateNonce(); + + vm.prank(_caller); + tokenProxy.invalidateNonce(); + + uint256 currentNonce = tokenProxy.nonces(_caller); + + assertEq(currentNonce, _initialNonce + 2, "Current nonce is incorrect"); + } +} + contract Transfer is PartialDelegationTest { function testFuzz_MovesVotesFromOneDelegateeSetToAnother( address _from,