Skip to content

Commit

Permalink
add UnorderedNonce interface
Browse files Browse the repository at this point in the history
  • Loading branch information
gretzke committed Nov 27, 2024
1 parent 43f4485 commit 309a35a
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 37 deletions.
10 changes: 4 additions & 6 deletions src/base/UnorderedNonce.sol
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {IUnorderedNonce} from "../interfaces/IUnorderedNonce.sol";

/// @title Unordered Nonce
/// @notice Contract state and methods for using unordered nonces in signatures
contract UnorderedNonce {
error NonceAlreadyUsed();

contract UnorderedNonce is IUnorderedNonce {
/// @notice mapping of nonces consumed by each address, where a nonce is a single bit on the 256-bit bitmap
/// @dev word is at most type(uint248).max
mapping(address owner => mapping(uint256 word => uint256 bitmap)) public nonces;
Expand All @@ -22,9 +22,7 @@ contract UnorderedNonce {
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
/// @inheritdoc IUnorderedNonce
function revokeNonce(uint256 nonce) external payable {
_useUnorderedNonce(msg.sender, nonce);
}
Expand Down
6 changes: 4 additions & 2 deletions src/interfaces/IPositionManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,18 @@ import {IERC721Permit_v4} from "./IERC721Permit_v4.sol";
import {IEIP712_v4} from "./IEIP712_v4.sol";
import {IMulticall_v4} from "./IMulticall_v4.sol";
import {IPoolInitializer} from "./IPoolInitializer.sol";
import {IUnorderedNonce} from "./IUnorderedNonce.sol";

/// @title IPositionManager
/// @notice Interface for the PositionManager contract

interface IPositionManager is
INotifier,
IImmutableState,
IERC721Permit_v4,
IEIP712_v4,
IMulticall_v4,
IPoolInitializer
IPoolInitializer,
IUnorderedNonce
{
/// @notice Thrown when the caller is not approved to modify a position
error NotApproved(address caller);
Expand Down
13 changes: 13 additions & 0 deletions src/interfaces/IUnorderedNonce.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @title UnorderedNonce Interface
/// @notice Interface for the UnorderedNonce contract
interface IUnorderedNonce {
error 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;
}
38 changes: 19 additions & 19 deletions test/UnorderedNonce.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pragma solidity ^0.8.20;

import "forge-std/Test.sol";
import {UnorderedNonce} from "../src/base/UnorderedNonce.sol";
import {UnorderedNonce, IUnorderedNonce} from "../src/base/UnorderedNonce.sol";
import {MockUnorderedNonce} from "./mocks/MockUnorderedNonce.sol";

contract UnorderedNonceTest is Test {
Expand All @@ -17,11 +17,11 @@ contract UnorderedNonceTest is Test {
unorderedNonce.spendNonce(address(this), 0);
unorderedNonce.spendNonce(address(this), 1);

vm.expectRevert(UnorderedNonce.NonceAlreadyUsed.selector);
vm.expectRevert(IUnorderedNonce.NonceAlreadyUsed.selector);
unorderedNonce.spendNonce(address(this), 1);
vm.expectRevert(UnorderedNonce.NonceAlreadyUsed.selector);
vm.expectRevert(IUnorderedNonce.NonceAlreadyUsed.selector);
unorderedNonce.spendNonce(address(this), 5);
vm.expectRevert(UnorderedNonce.NonceAlreadyUsed.selector);
vm.expectRevert(IUnorderedNonce.NonceAlreadyUsed.selector);
unorderedNonce.spendNonce(address(this), 0);
unorderedNonce.spendNonce(address(this), 4);
}
Expand All @@ -30,19 +30,19 @@ contract UnorderedNonceTest is Test {
unorderedNonce.spendNonce(address(this), 255);
unorderedNonce.spendNonce(address(this), 256);

vm.expectRevert(UnorderedNonce.NonceAlreadyUsed.selector);
vm.expectRevert(IUnorderedNonce.NonceAlreadyUsed.selector);
unorderedNonce.spendNonce(address(this), 255);
vm.expectRevert(UnorderedNonce.NonceAlreadyUsed.selector);
vm.expectRevert(IUnorderedNonce.NonceAlreadyUsed.selector);
unorderedNonce.spendNonce(address(this), 256);
}

function testHighNonces() public {
unorderedNonce.spendNonce(address(this), 2 ** 240);
unorderedNonce.spendNonce(address(this), 2 ** 240 + 1);

vm.expectRevert(UnorderedNonce.NonceAlreadyUsed.selector);
vm.expectRevert(IUnorderedNonce.NonceAlreadyUsed.selector);
unorderedNonce.spendNonce(address(this), 2 ** 240);
vm.expectRevert(UnorderedNonce.NonceAlreadyUsed.selector);
vm.expectRevert(IUnorderedNonce.NonceAlreadyUsed.selector);
unorderedNonce.spendNonce(address(this), 2 ** 240 + 1);

unorderedNonce.spendNonce(address(this), 2 ** 240 + 2);
Expand All @@ -51,13 +51,13 @@ contract UnorderedNonceTest is Test {
function testInvalidateFullWord() public {
unorderedNonce.batchSpendNonces(0, 2 ** 256 - 1);

vm.expectRevert(UnorderedNonce.NonceAlreadyUsed.selector);
vm.expectRevert(IUnorderedNonce.NonceAlreadyUsed.selector);
unorderedNonce.spendNonce(address(this), 0);
vm.expectRevert(UnorderedNonce.NonceAlreadyUsed.selector);
vm.expectRevert(IUnorderedNonce.NonceAlreadyUsed.selector);
unorderedNonce.spendNonce(address(this), 1);
vm.expectRevert(UnorderedNonce.NonceAlreadyUsed.selector);
vm.expectRevert(IUnorderedNonce.NonceAlreadyUsed.selector);
unorderedNonce.spendNonce(address(this), 254);
vm.expectRevert(UnorderedNonce.NonceAlreadyUsed.selector);
vm.expectRevert(IUnorderedNonce.NonceAlreadyUsed.selector);
unorderedNonce.spendNonce(address(this), 255);
unorderedNonce.spendNonce(address(this), 256);
}
Expand All @@ -68,9 +68,9 @@ contract UnorderedNonceTest is Test {
unorderedNonce.spendNonce(address(this), 0);
unorderedNonce.spendNonce(address(this), 254);
unorderedNonce.spendNonce(address(this), 255);
vm.expectRevert(UnorderedNonce.NonceAlreadyUsed.selector);
vm.expectRevert(IUnorderedNonce.NonceAlreadyUsed.selector);
unorderedNonce.spendNonce(address(this), 256);
vm.expectRevert(UnorderedNonce.NonceAlreadyUsed.selector);
vm.expectRevert(IUnorderedNonce.NonceAlreadyUsed.selector);
unorderedNonce.spendNonce(address(this), 511);
unorderedNonce.spendNonce(address(this), 512);
}
Expand All @@ -87,21 +87,21 @@ contract UnorderedNonceTest is Test {
nonce = bound(nonce, 0, (word + 2) * 256);

if ((word * 256) <= nonce && nonce < ((word + 1) * 256)) {
vm.expectRevert(UnorderedNonce.NonceAlreadyUsed.selector);
vm.expectRevert(IUnorderedNonce.NonceAlreadyUsed.selector);
}
unorderedNonce.spendNonce(address(this), nonce);
}

function test_fuzz_UsingNonceTwiceFails(uint256 nonce) public {
unorderedNonce.spendNonce(address(this), nonce);
vm.expectRevert(UnorderedNonce.NonceAlreadyUsed.selector);
vm.expectRevert(IUnorderedNonce.NonceAlreadyUsed.selector);
unorderedNonce.spendNonce(address(this), nonce);
}

function test_fuzz_UseTwoRandomNonces(uint256 first, uint256 second) public {
unorderedNonce.spendNonce(address(this), first);
if (first == second) {
vm.expectRevert(UnorderedNonce.NonceAlreadyUsed.selector);
vm.expectRevert(IUnorderedNonce.NonceAlreadyUsed.selector);
unorderedNonce.spendNonce(address(this), second);
} else {
unorderedNonce.spendNonce(address(this), second);
Expand All @@ -110,13 +110,13 @@ contract UnorderedNonceTest is Test {

function test_fuzz_revokeNonce(uint256 nonce) public {
unorderedNonce.revokeNonce(nonce);
vm.expectRevert(UnorderedNonce.NonceAlreadyUsed.selector);
vm.expectRevert(IUnorderedNonce.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);
if (first == second) vm.expectRevert(IUnorderedNonce.NonceAlreadyUsed.selector);
unorderedNonce.revokeNonce(second);
}
}
6 changes: 3 additions & 3 deletions test/erc721Permit/ERC721Permit.permit.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {ERC721PermitHash} from "../../src/libraries/ERC721PermitHash.sol";
import {MockERC721Permit} from "../mocks/MockERC721Permit.sol";
import {IERC721Permit_v4} from "../../src/interfaces/IERC721Permit_v4.sol";
import {IERC721} from "forge-std/interfaces/IERC721.sol";
import {UnorderedNonce} from "../../src/base/UnorderedNonce.sol";
import {IUnorderedNonce} from "../../src/interfaces/IUnorderedNonce.sol";

contract ERC721PermitTest is Test {
MockERC721Permit erc721Permit;
Expand Down Expand Up @@ -169,7 +169,7 @@ contract ERC721PermitTest is Test {
bytes memory signature = abi.encodePacked(r, s, v);

vm.startPrank(alice);
vm.expectRevert(UnorderedNonce.NonceAlreadyUsed.selector);
vm.expectRevert(IUnorderedNonce.NonceAlreadyUsed.selector);
erc721Permit.permit(bob, tokenIdAlice, block.timestamp, nonce, signature);
vm.stopPrank();
}
Expand All @@ -192,7 +192,7 @@ contract ERC721PermitTest is Test {
bytes memory signature = abi.encodePacked(r, s, v);

vm.startPrank(alice);
vm.expectRevert(UnorderedNonce.NonceAlreadyUsed.selector);
vm.expectRevert(IUnorderedNonce.NonceAlreadyUsed.selector);
erc721Permit.permit(bob, tokenIdAlice2, block.timestamp, nonce, signature);
vm.stopPrank();
}
Expand Down
8 changes: 4 additions & 4 deletions test/erc721Permit/ERC721Permit.permitForAll.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {ERC721PermitHash} from "../../src/libraries/ERC721PermitHash.sol";
import {MockERC721Permit} from "../mocks/MockERC721Permit.sol";
import {IERC721Permit_v4} from "../../src/interfaces/IERC721Permit_v4.sol";
import {IERC721} from "forge-std/interfaces/IERC721.sol";
import {UnorderedNonce} from "../../src/base/UnorderedNonce.sol";
import {IUnorderedNonce} from "../../src/interfaces/IUnorderedNonce.sol";

contract ERC721PermitForAllTest is Test {
MockERC721Permit erc721Permit;
Expand Down Expand Up @@ -141,7 +141,7 @@ contract ERC721PermitForAllTest is Test {
bytes memory signature = abi.encodePacked(r, s, v);

vm.startPrank(alice);
vm.expectRevert(UnorderedNonce.NonceAlreadyUsed.selector);
vm.expectRevert(IUnorderedNonce.NonceAlreadyUsed.selector);
erc721Permit.permitForAll(alice, bob, true, block.timestamp, nonce, signature);
vm.stopPrank();
}
Expand Down Expand Up @@ -292,7 +292,7 @@ contract ERC721PermitForAllTest is Test {

// Nonce does not work with permitForAll
vm.startPrank(bob);
vm.expectRevert(UnorderedNonce.NonceAlreadyUsed.selector);
vm.expectRevert(IUnorderedNonce.NonceAlreadyUsed.selector);
erc721Permit.permitForAll(alice, bob, true, deadline, nonce, signature);
vm.stopPrank();
}
Expand All @@ -311,7 +311,7 @@ contract ERC721PermitForAllTest is Test {

// Nonce does not work with permitForAll
vm.startPrank(bob);
vm.expectRevert(UnorderedNonce.NonceAlreadyUsed.selector);
vm.expectRevert(IUnorderedNonce.NonceAlreadyUsed.selector);
erc721Permit.permitForAll(alice, bob, true, deadline, nonce, signature);
vm.stopPrank();
}
Expand Down
6 changes: 3 additions & 3 deletions test/position-managers/Permit.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {SignatureVerification} from "permit2/src/libraries/SignatureVerification
import {IERC20} from "forge-std/interfaces/IERC20.sol";
import {IERC721Permit_v4} from "../../src/interfaces/IERC721Permit_v4.sol";
import {ERC721Permit_v4} from "../../src/base/ERC721Permit_v4.sol";
import {UnorderedNonce} from "../../src/base/UnorderedNonce.sol";
import {IUnorderedNonce} from "../../src/interfaces/IUnorderedNonce.sol";

import {PositionConfig} from "../shared/PositionConfig.sol";
import {IPositionManager} from "../../src/interfaces/IPositionManager.sol";
Expand Down Expand Up @@ -226,15 +226,15 @@ contract PermitTest is Test, PosmTestSetup {

// alice revokes the nonce
vm.prank(alice);
UnorderedNonce(address(lpm)).revokeNonce(nonce);
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);
vm.expectRevert(IUnorderedNonce.NonceAlreadyUsed.selector);
lpm.permit(bob, tokenIdAlice, block.timestamp + 1, nonce, signature);
}

Expand Down

0 comments on commit 309a35a

Please sign in to comment.