Skip to content

Commit

Permalink
feat: cleanup scoring code
Browse files Browse the repository at this point in the history
  • Loading branch information
marktoda committed Oct 18, 2024
1 parent 07657f4 commit d70e668
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 59 deletions.
120 changes: 66 additions & 54 deletions src/libraries/VanityAddressLib.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import {console2} from "forge-std/console2.sol";

/// @title VanityAddressLib
/// @notice A library to score addresses based on their vanity
library VanityAddressLib {
Expand All @@ -16,72 +18,82 @@ library VanityAddressLib {
/// @param addr The address to score
/// @return calculatedScore The vanity score of the address
function score(address addr) internal pure returns (uint256 calculatedScore) {
// 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
// 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

// convert the address to bytes for easier parsing
bytes20 addrBytes = bytes20(addr);

bool startingZeros = true;
bool startingFours = true;
bool firstFour = true;
uint8 fourCounts; // counter for the number of 4s
// iterate over the nibbles of the address
for (uint256 i = 0; i < addrBytes.length * 2; i++) {
uint8 currentNibble;
if (i % 2 == 0) {
// Get the higher nibble of the byte
currentNibble = uint8(addrBytes[i / 2] >> 4);
} else {
// Get the lower nibble of the byte
currentNibble = uint8(addrBytes[i / 2] & 0x0F);
}
// 10 points per leading zero nibble
uint256 leadingZeroCount = getLeadingNibbleCount(addrBytes, 0, 0);
calculatedScore += (leadingZeroCount * 10);

// leading 0s
if (startingZeros && currentNibble == 0) {
calculatedScore += 10;
continue;
} else {
startingZeros = false;
}

// leading 4s
if (startingFours) {
// If the first nonzero nibble is not 4, the score is an automatic 0
if (firstFour && currentNibble != 4) {
return 0;
}
// special handling for 4s immediatley 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;
}

if (currentNibble == 4) {
fourCounts += 1;
if (fourCounts == 4) {
calculatedScore += 40;
// If the leading 4 4s are also the last 4 nibbles, add 20 points
if (i == addrBytes.length * 2 - 1) {
calculatedScore += 20;
}
}
} else {
// If the first nibble after the 4 4s is not a 4, add 20 points
if (fourCounts == 4) {
calculatedScore += 20;
}
startingFours = false;
}
firstFour = false;
}
// handling for remaining nibbles
// for (uint256 i = leadingZeroCount + leadingFourCount; i < addrBytes.length * 2; i++) {
for (uint256 i = 0; i < addrBytes.length * 2; i++) {
uint8 currentNibble = getNibble(addrBytes, i);

// count each 4 nibble separately
// 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] & 0xFF == 0x44 && addrBytes[19] & 0xFF == 0x44) {
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;
}
}
}
9 changes: 4 additions & 5 deletions test/libraries/VanityAddressLib.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@ import {Test, console} from "forge-std/Test.sol";
import {VanityAddressLib} from "../../src/libraries/VanityAddressLib.sol";

contract VanityAddressLibTest is Test {
function test_scoreAllZeros() public pure {
address addr = address(0);
uint256 score = VanityAddressLib.score(addr);
uint256 expected = 400; // not a 4 after the zeros
assertEq(score, expected);
function test_fuzz_reasonableScoreNeverReverts(address test) public pure {
uint256 score = VanityAddressLib.score(address(test));
assertGe(score, 0);
assertLe(score, 444);
}

function test_scoreAllFours() public pure {
Expand Down

0 comments on commit d70e668

Please sign in to comment.