Skip to content

Commit

Permalink
merge main, fix test
Browse files Browse the repository at this point in the history
  • Loading branch information
snreynolds committed Nov 18, 2024
2 parents 2b337de + 3f295d8 commit a8a197f
Show file tree
Hide file tree
Showing 10 changed files with 491 additions and 4 deletions.
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_Bytecode.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
7158
7117
2 changes: 1 addition & 1 deletion .forge-snapshots/positionDescriptor bytecode size.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
31728
31806
1 change: 1 addition & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ fs_permissions = [{ access = "read-write", path = ".forge-snapshots/"}]
evm_version = "cancun"
gas_limit = "3000000000"
fuzz_runs = 10_000
bytecode_hash = "none"

[profile.debug]
via_ir = false
Expand Down
85 changes: 85 additions & 0 deletions src/UniswapV4DeployerCompetition.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// SPADIX-License-Identifier: UNLICENSED
pragma solidity 0.8.26;

import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";
import {VanityAddressLib} from "./libraries/VanityAddressLib.sol";
import {IUniswapV4DeployerCompetition} from "./interfaces/IUniswapV4DeployerCompetition.sol";

/// @title UniswapV4DeployerCompetition
/// @notice A contract to crowdsource a salt for the best Uniswap V4 address
contract UniswapV4DeployerCompetition is IUniswapV4DeployerCompetition {
using VanityAddressLib for address;

/// @dev The salt for the best address found so far
bytes32 public bestAddressSalt;
/// @dev The submitter of the best address found so far
address public bestAddressSubmitter;

/// @dev The deadline for the competition
uint256 public immutable competitionDeadline;
/// @dev The init code hash of the V4 contract
bytes32 public immutable initCodeHash;

/// @dev The deployer who can initiate the deployment of the v4 PoolManager, until the exclusive deploy deadline.
/// @dev After this deadline anyone can deploy.
address public immutable deployer;
/// @dev The deadline for exclusive deployment by deployer after deadline
uint256 public immutable exclusiveDeployDeadline;

constructor(
bytes32 _initCodeHash,
uint256 _competitionDeadline,
address _exclusiveDeployer,
uint256 _exclusiveDeployLength
) {
initCodeHash = _initCodeHash;
competitionDeadline = _competitionDeadline;
exclusiveDeployDeadline = _competitionDeadline + _exclusiveDeployLength;
deployer = _exclusiveDeployer;
}

/// @inheritdoc IUniswapV4DeployerCompetition
function updateBestAddress(bytes32 salt) external {
if (block.timestamp > competitionDeadline) {
revert CompetitionOver(block.timestamp, competitionDeadline);
}

address saltSubAddress = address(bytes20(salt));
if (saltSubAddress != msg.sender && saltSubAddress != address(0)) revert InvalidSender(salt, msg.sender);

address newAddress = Create2.computeAddress(salt, initCodeHash);
address _bestAddress = bestAddress();
if (!newAddress.betterThan(_bestAddress)) {
revert WorseAddress(newAddress, _bestAddress, newAddress.score(), _bestAddress.score());
}

bestAddressSalt = salt;
bestAddressSubmitter = msg.sender;

emit NewAddressFound(newAddress, msg.sender, newAddress.score());
}

/// @inheritdoc IUniswapV4DeployerCompetition
function deploy(bytes memory bytecode) external {
if (keccak256(bytecode) != initCodeHash) {
revert InvalidBytecode();
}

if (block.timestamp <= competitionDeadline) {
revert CompetitionNotOver(block.timestamp, competitionDeadline);
}

if (msg.sender != deployer && block.timestamp <= exclusiveDeployDeadline) {
// anyone can deploy after the deadline
revert NotAllowedToDeploy(msg.sender, deployer);
}

// the owner of the contract must be encoded in the bytecode
Create2.deploy(0, bestAddressSalt, bytecode);
}

/// @dev returns the best address found so far
function bestAddress() public view returns (address) {
return Create2.computeAddress(bestAddressSalt, initCodeHash);
}
}
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 submitter, uint256 score);

error InvalidBytecode();
error CompetitionNotOver(uint256 currentTime, uint256 deadline);
error CompetitionOver(uint256 currentTime, uint256 deadline);
error NotAllowedToDeploy(address sender, address deployer);
error WorseAddress(address newAddress, address bestAddress, uint256 newScore, uint256 bestScore);
error InvalidSender(bytes32 salt, address sender);

/// @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
/// @dev The first 20 bytes of the salt must be either address(0) or msg.sender
function updateBestAddress(bytes32 salt) external;

/// @notice deploys 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;
}
97 changes: 97 additions & 0 deletions src/libraries/VanityAddressLib.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

/// @title VanityAddressLib
/// @notice A library to score addresses based on their vanity
library VanityAddressLib {
/// @notice Compares two addresses and returns true if the first address has a better vanity score
/// @param first The first address to compare
/// @param second The second address to compare
/// @return better True if the first address has a better vanity score
function betterThan(address first, address second) internal pure returns (bool better) {
return score(first) > score(second);
}

/// @notice Scores an address based on its vanity
/// @dev Scoring rules:
/// Requirement: The first nonzero nibble must be 4
/// 10 points for every leading 0 nibble
/// 40 points if the first 4 is followed by 3 more 4s
/// 20 points if the first nibble after the 4 4s is NOT a 4
/// 20 points if the last 4 nibbles are 4s
/// 1 point for every 4
/// @param addr The address to score
/// @return calculatedScore The vanity score of the address
function score(address addr) internal pure returns (uint256 calculatedScore) {
// convert the address to bytes for easier parsing
bytes20 addrBytes = bytes20(addr);

unchecked {
// 10 points per leading zero nibble
uint256 leadingZeroCount = getLeadingNibbleCount(addrBytes, 0, 0);
calculatedScore += (leadingZeroCount * 10);

// special handling for 4s immediately after leading 0s
uint256 leadingFourCount = getLeadingNibbleCount(addrBytes, leadingZeroCount, 4);
// If the first nonzero nibble is not 4, return 0
if (leadingFourCount == 0) {
return 0;
} else if (leadingFourCount == 4) {
// 60 points if exactly 4 4s
calculatedScore += 60;
} else if (leadingFourCount > 4) {
// 40 points if more than 4 4s
calculatedScore += 40;
}

// handling for remaining nibbles
for (uint256 i = 0; i < addrBytes.length * 2; i++) {
uint8 currentNibble = getNibble(addrBytes, i);

// 1 extra point for any 4 nibbles
if (currentNibble == 4) {
calculatedScore += 1;
}
}

// If the last 4 nibbles are 4s, add 20 points
if (addrBytes[18] == 0x44 && addrBytes[19] == 0x44) {
calculatedScore += 20;
}
}
}

/// @notice Returns the number of leading nibbles in an address that match a given value
/// @param addrBytes The address to count the leading zero nibbles in
function getLeadingNibbleCount(bytes20 addrBytes, uint256 startIndex, uint8 comparison)
internal
pure
returns (uint256 count)
{
if (startIndex >= addrBytes.length * 2) {
return count;
}

for (uint256 i = startIndex; i < addrBytes.length * 2; i++) {
uint8 currentNibble = getNibble(addrBytes, i);
if (currentNibble != comparison) {
return count;
}
count += 1;
}
}

/// @notice Returns the nibble at a given index in an address
/// @param input The address to get the nibble from
/// @param nibbleIndex The index of the nibble to get
function getNibble(bytes20 input, uint256 nibbleIndex) internal pure returns (uint8 currentNibble) {
uint8 currByte = uint8(input[nibbleIndex / 2]);
if (nibbleIndex % 2 == 0) {
// Get the higher nibble of the byte
currentNibble = currByte >> 4;
} else {
// Get the lower nibble of the byte
currentNibble = currByte & 0x0F;
}
}
}
Loading

0 comments on commit a8a197f

Please sign in to comment.