Skip to content

Commit

Permalink
interface plus tests
Browse files Browse the repository at this point in the history
  • Loading branch information
dianakocsis committed Oct 7, 2024
1 parent 5db7a3a commit 1af2165
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 29 deletions.
38 changes: 14 additions & 24 deletions src/UniswapV4DeployerCompetition.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,11 @@ import {Owned} from "solmate/src/auth/Owned.sol";
import {ERC721} from "solmate/src/tokens/ERC721.sol";
import {TokenURILib} from "./libraries/UniswapV4DeployerTokenURILib.sol";
import {VanityAddressLib} from "./libraries/VanityAddressLib.sol";
import {IUniswapV4DeployerCompetition} from "./interfaces/IUniswapV4DeployerCompetition.sol";

/// @title UniswapV4DeployerCompetition
/// @notice A competition to deploy the UniswapV4 contract with the best address
contract UniswapV4DeployerCompetition is ERC721 {
contract UniswapV4DeployerCompetition is ERC721, IUniswapV4DeployerCompetition {
using VanityAddressLib for address;

event NewAddressFound(address bestAddress, address minter, uint256 score);

error InvalidBytecode();
error CompetitionNotOver();
error CompetitionOver();
error NotAllowedToDeploy();
error BountyTransferFailed();
error WorseAddress();

bytes32 public bestAddressSalt;
address public bestAddress;
address public bestAddressSender;
Expand All @@ -35,15 +25,13 @@ contract UniswapV4DeployerCompetition is ERC721 {
v4Owner = _v4Owner;
}

/// @notice Updates the best address if the new address has a better vanity score
/// @param salt The salt to use to compute the new address with CREATE2
function updateBestAddress(bytes32 salt) external {
function updateBestAddress(bytes32 salt) external override {
if (block.timestamp > competitionDeadline) {
revert CompetitionOver();
revert CompetitionOver(block.timestamp, competitionDeadline);
}
address newAddress = Create2.computeAddress(salt, initCodeHash, address(this));
if (bestAddress != address(0) && !newAddress.betterThan(bestAddress)) {
revert WorseAddress();
revert WorseAddress(newAddress, bestAddress, newAddress.score(), bestAddress.score());
}

bestAddress = newAddress;
Expand All @@ -53,18 +41,15 @@ contract UniswapV4DeployerCompetition is ERC721 {
emit NewAddressFound(newAddress, msg.sender, newAddress.score());
}

/// @notice Allows the winner to deploy the Uniswap v4 PoolManager contract
/// @param bytecode The bytecode of the Uniswap v4 PoolManager contract
/// @dev The bytecode must match the initCodeHash
function deploy(bytes memory bytecode) external {
function deploy(bytes memory bytecode) external override {
if (keccak256(bytecode) != initCodeHash) {
revert InvalidBytecode();
}
if (block.timestamp < competitionDeadline) {
revert CompetitionNotOver();
revert CompetitionNotOver(block.timestamp, competitionDeadline);
}
if (msg.sender != bestAddressSender && block.timestamp < exclusiveDeployDeadline) {
revert NotAllowedToDeploy(); // anyone can deploy after the deadline
revert NotAllowedToDeploy(msg.sender, bestAddressSender); // anyone can deploy after the deadline
}
Create2.deploy(0, bestAddressSalt, bytecode);

Expand All @@ -80,7 +65,12 @@ contract UniswapV4DeployerCompetition is ERC721 {
}

/// @notice Returns the URI for the token
function tokenURI(uint256) public pure override returns (string memory) {
/// @param id The token id
/// @return The URI for the token
function tokenURI(uint256 id) public pure override returns (string memory) {
if (id != 0) {
revert InvalidTokenId(id);
}
return TokenURILib.tokenURI();
}
}
25 changes: 25 additions & 0 deletions src/interfaces/IUniswapV4DeployerCompetition.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPADIX-License-Identifier: UNLICENSED
pragma solidity 0.8.26;

/// @title UniswapV4DeployerCompetition
/// @notice A competition to deploy the UniswapV4 contract with the best address
interface IUniswapV4DeployerCompetition {
event NewAddressFound(address indexed bestAddress, address indexed minter, uint256 score);

error InvalidBytecode();
error CompetitionNotOver(uint256 currentTime, uint256 deadline);
error CompetitionOver(uint256 currentTime, uint256 deadline);
error NotAllowedToDeploy(address sender, address bestAddressSender);
error BountyTransferFailed();
error WorseAddress(address newAddress, address bestAddress, uint256 newScore, uint256 bestScore);
error InvalidTokenId(uint256 tokenId);

/// @notice Updates the best address if the new address has a better vanity score
/// @param salt The salt to use to compute the new address with CREATE2
function updateBestAddress(bytes32 salt) external;

/// @notice Allows the winner to deploy the Uniswap v4 PoolManager contract
/// @param bytecode The bytecode of the Uniswap v4 PoolManager contract
/// @dev The bytecode must match the initCodeHash
function deploy(bytes memory bytecode) external;
}
69 changes: 64 additions & 5 deletions test/UniswapV4DeployerCompetition.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@ import {Test, console2} from "forge-std/Test.sol";
import {PoolManager} from "@uniswap/v4-core/src/PoolManager.sol";
import {UniswapV4DeployerCompetition} from "../src/UniswapV4DeployerCompetition.sol";
import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol";
import {VanityAddressLib} from "../src/libraries/VanityAddressLib.sol";
import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";
import {IUniswapV4DeployerCompetition} from "../src/interfaces/IUniswapV4DeployerCompetition.sol";

contract UniswapV4DeployerCompetitionTest is Test {
using VanityAddressLib for address;

UniswapV4DeployerCompetition deployer;
bytes32 initCodeHash;
address v4Owner;
Expand All @@ -27,7 +32,11 @@ contract UniswapV4DeployerCompetitionTest is Test {
assertEq(deployer.bestAddressSender(), address(0));
assertEq(deployer.bestAddressSalt(), bytes32(0));

address newAddress = Create2.computeAddress(salt, initCodeHash, address(deployer));

vm.prank(winner);
vm.expectEmit(true, true, true, false, address(deployer));
emit IUniswapV4DeployerCompetition.NewAddressFound(newAddress, winner, VanityAddressLib.score(newAddress));
deployer.updateBestAddress(salt);
assertEq(address(deployer).balance, 1 ether);
assertFalse(deployer.bestAddress() == address(0));
Expand All @@ -47,7 +56,11 @@ contract UniswapV4DeployerCompetitionTest is Test {

function testCompetitionOver(bytes32 salt) public {
vm.warp(deployer.competitionDeadline() + 1);
vm.expectRevert(UniswapV4DeployerCompetition.CompetitionOver.selector);
vm.expectRevert(
abi.encodeWithSelector(
IUniswapV4DeployerCompetition.CompetitionOver.selector, block.timestamp, deployer.competitionDeadline()
)
);
deployer.updateBestAddress(salt);
}

Expand All @@ -68,26 +81,62 @@ contract UniswapV4DeployerCompetitionTest is Test {
vm.prank(winner);
deployer.updateBestAddress(salt);
vm.warp(timestamp);
vm.expectRevert(UniswapV4DeployerCompetition.CompetitionNotOver.selector);
vm.expectRevert(
abi.encodeWithSelector(
IUniswapV4DeployerCompetition.CompetitionNotOver.selector, timestamp, deployer.competitionDeadline()
)
);
deployer.deploy(abi.encodePacked(type(PoolManager).creationCode, controllerGasLimit));
}

function testInvalidBytecode(bytes32 salt) public {
vm.prank(winner);
deployer.updateBestAddress(salt);
vm.expectRevert(UniswapV4DeployerCompetition.InvalidBytecode.selector);
vm.expectRevert(IUniswapV4DeployerCompetition.InvalidBytecode.selector);
deployer.deploy(abi.encodePacked(type(PoolManager).creationCode, controllerGasLimit + 1));
}

function testInvalidMsgSender(bytes32 salt) public {
vm.prank(winner);
deployer.updateBestAddress(salt);
vm.warp(deployer.competitionDeadline() + 1);
vm.prank(address(1));
vm.expectRevert(
abi.encodeWithSelector(IUniswapV4DeployerCompetition.NotAllowedToDeploy.selector, address(1), winner)
);
deployer.deploy(abi.encodePacked(type(PoolManager).creationCode, controllerGasLimit));
}

function testAfterExcusiveDeployDeadline(bytes32 salt) public {
vm.prank(winner);
deployer.updateBestAddress(salt);
vm.warp(deployer.exclusiveDeployDeadline() + 1);
vm.prank(address(1));
deployer.deploy(abi.encodePacked(type(PoolManager).creationCode, controllerGasLimit));
assertEq(address(deployer).balance, 0 ether);
assertEq(winner.balance, 1 ether);
}

function testEqualSaltNotChanged(bytes32 salt) public {
vm.prank(winner);
deployer.updateBestAddress(salt);
assertEq(deployer.bestAddressSender(), winner);
assertEq(deployer.bestAddressSalt(), salt);

address newAddr = Create2.computeAddress(salt >> 1, initCodeHash, address(deployer));
vm.assume(deployer.bestAddress().betterThan(newAddr));

vm.prank(address(1));
vm.expectRevert(UniswapV4DeployerCompetition.WorseAddress.selector);
deployer.updateBestAddress(salt);
vm.expectRevert(
abi.encodeWithSelector(
IUniswapV4DeployerCompetition.WorseAddress.selector,
newAddr,
deployer.bestAddress(),
newAddr.score(),
deployer.bestAddress().score()
)
);
deployer.updateBestAddress(salt >> 1);
}

function testUpdateNotEqual() public {
Expand All @@ -101,4 +150,14 @@ contract UniswapV4DeployerCompetitionTest is Test {
assertEq(deployer.bestAddressSender(), winner);
assertEq(deployer.bestAddressSalt(), salt2);
}

function testTokenURI(bytes32 salt) public {
vm.prank(winner);
deployer.updateBestAddress(salt);
vm.warp(deployer.competitionDeadline() + 1);
vm.prank(winner);
deployer.deploy(abi.encodePacked(type(PoolManager).creationCode, controllerGasLimit));
vm.expectRevert(abi.encodeWithSelector(IUniswapV4DeployerCompetition.InvalidTokenId.selector, 1));
deployer.tokenURI(1);
}
}

0 comments on commit 1af2165

Please sign in to comment.