Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OZ-L01: Revokable Unordered Nonces #304

Merged
merged 3 commits into from
Aug 28, 2024
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
Original file line number Diff line number Diff line change
@@ -1 +1 @@
421127
421104
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_permit.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
79484
79506
Original file line number Diff line number Diff line change
@@ -1 +1 @@
62372
62394
2 changes: 1 addition & 1 deletion .forge-snapshots/PositionManager_permit_twice.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
45260
45282
7 changes: 7 additions & 0 deletions src/base/UnorderedNonce.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,11 @@ contract UnorderedNonce {
uint256 flipped = nonces[owner][wordPos] ^= bit;
if (flipped & bit == 0) revert NonceAlreadyUsed();
}

/// @notice Revoke a nonce by spending it, preventing it from being used again
/// @dev Used in cases where a valid nonce has not been broadcasted onchain, and the owner wants to revoke the validity of the nonce
/// @dev payable so it can be multicalled with native-token related actions
function revokeNonce(uint256 nonce) external payable {
_useUnorderedNonce(msg.sender, nonce);
}
}
12 changes: 12 additions & 0 deletions test/UnorderedNonce.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -107,4 +107,16 @@ contract UnorderedNonceTest is Test {
unorderedNonce.spendNonce(address(this), second);
}
}

function test_fuzz_revokeNonce(uint256 nonce) public {
unorderedNonce.revokeNonce(nonce);
vm.expectRevert(UnorderedNonce.NonceAlreadyUsed.selector);
unorderedNonce.revokeNonce(nonce);
}

function test_fuzz_revokeNonce_twoNonces(uint256 first, uint256 second) public {
unorderedNonce.revokeNonce(first);
if (first == second) vm.expectRevert(UnorderedNonce.NonceAlreadyUsed.selector);
unorderedNonce.revokeNonce(second);
}
}
19 changes: 19 additions & 0 deletions test/erc721Permit/ERC721Permit.permitForAll.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,25 @@ contract ERC721PermitForAllTest is Test {
vm.stopPrank();
}

/// @notice revoking a nonce prevents it from being used in permitForAll()
function test_fuzz_erc721PermitForAll_revokedNonceUsed(uint256 nonce) public {
// alice revokes the nonce
vm.prank(alice);
erc721Permit.revokeNonce(nonce);

uint256 deadline = block.timestamp;
bytes32 digest = _getPermitForAllDigest(bob, true, nonce, deadline);
// alice signs a permit for bob
(uint8 v, bytes32 r, bytes32 s) = vm.sign(alicePK, digest);
bytes memory signature = abi.encodePacked(r, s, v);

// Nonce does not work with permitForAll
vm.startPrank(bob);
vm.expectRevert(UnorderedNonce.NonceAlreadyUsed.selector);
erc721Permit.permitForAll(alice, bob, true, deadline, nonce, signature);
vm.stopPrank();
}

// Helpers related to permitForAll
function _permitForAll(uint256 privateKey, address owner, address operator, bool approved, uint256 nonce)
internal
Expand Down
21 changes: 21 additions & 0 deletions test/position-managers/Permit.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,27 @@ contract PermitTest is Test, PosmTestSetup {
vm.stopPrank();
}

/// @notice revoking a nonce prevents it from being used in permit()
function test_fuzz_noPermit_revokeRevert(uint256 nonce) public {
uint256 liquidityAlice = 1e18;
vm.prank(alice);
mint(config, liquidityAlice, alice, ZERO_BYTES);
uint256 tokenIdAlice = lpm.nextTokenId() - 1;

// alice revokes the nonce
vm.prank(alice);
lpm.revokeNonce(nonce);

// alice gives bob spender permissions
bytes32 digest = getDigest(bob, tokenIdAlice, nonce, block.timestamp + 1);

(uint8 v, bytes32 r, bytes32 s) = vm.sign(alicePK, digest);
bytes memory signature = abi.encodePacked(r, s, v);

vm.expectRevert(UnorderedNonce.NonceAlreadyUsed.selector);
lpm.permit(bob, tokenIdAlice, block.timestamp + 1, nonce, signature);
}

// Bob can use alice's signature to permit & decrease liquidity
function test_permit_operatorSelfPermit() public {
uint256 liquidityAlice = 1e18;
Expand Down
Loading