Skip to content

Commit

Permalink
Merge pull request #117 from Hats-Protocol/fix-review
Browse files Browse the repository at this point in the history
Sherlock fix review
  • Loading branch information
nintynick authored Mar 18, 2023
2 parents 9d6e7d4 + 20e664a commit 7509b70
Show file tree
Hide file tree
Showing 9 changed files with 580 additions and 138 deletions.
2 changes: 1 addition & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ src = 'src'
out = 'out'
libs = ['lib']
optimizer_runs = 10_000
gas_reports = ["Hats"]
gas_reports = ["Hats", "HatsIdUtilities"]
auto_detect_solc = false
solc = "0.8.17"
remappings = [
Expand Down
287 changes: 214 additions & 73 deletions src/Hats.sol

Large diffs are not rendered by default.

30 changes: 30 additions & 0 deletions src/HatsIdUtilities.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@
pragma solidity >=0.8.13;

import "./Interfaces/IHatsIdUtilities.sol";
// import { console2 } from "forge-std/Test.sol"; //remove after testing

/// @notice see HatsErrors.sol for description
error MaxLevelsReached();

/// @title Hats Id Utilities
/// @dev Functions for working with Hat Ids from Hats Protocol. Factored out of Hats.sol
Expand Down Expand Up @@ -58,6 +62,7 @@ contract HatsIdUtilities is IHatsIdUtilities {
uint256 internal constant MAX_LEVELS = 14;

/// @notice Constructs a valid hat id for a new hat underneath a given admin
/// @dev Reverts if the admin has already reached `MAX_LEVELS`
/// @param _admin the id of the admin for the new hat
/// @param _newHat the uint16 id of the new hat
/// @return id The constructed hat id
Expand Down Expand Up @@ -88,6 +93,9 @@ contract HatsIdUtilities is IHatsIdUtilities {
++i;
}
}

// if _admin is already at MAX_LEVELS, child hats are not possible, so we revert
revert MaxLevelsReached();
}

/// @notice Identifies the level a given hat in its hat tree
Expand Down Expand Up @@ -142,6 +150,28 @@ contract HatsIdUtilities is IHatsIdUtilities {
_isLocalTopHat = _hatId > 0 && uint224(_hatId) == 0;
}

function isValidHatId(uint256 _hatId) public pure returns (bool validHatId) {
// valid top hats are valid hats
if (isLocalTopHat(_hatId)) return true;

uint32 level = getLocalHatLevel(_hatId);
uint256 admin;
// for each subsequent level up the tree, check if the level is 0 and return false if so
for (uint256 i = level - 1; i > 0;) {
// truncate to find the (truncated) admin at this level
// we don't need to check _hatId's own level since getLocalHatLevel already ensures that its non-empty
admin = _hatId >> (LOWER_LEVEL_ADDRESS_SPACE * (MAX_LEVELS - i));
// if the lowest level of the truncated admin is empty, the hat id is invalid
if (uint16(admin) == 0) return false;

unchecked {
--i;
}
}
// if there are no empty levels, return true
return true;
}

/// @notice Gets the hat id of the admin at a given level of a given hat
/// @dev This function traverses trees by following the linkedTreeAdmin
/// pointer to a hat located in a different tree
Expand Down
13 changes: 12 additions & 1 deletion src/Interfaces/HatsErrors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,18 @@ interface HatsErrors {
/// @notice Emitted when attempting to create a hat with a level 14 hat as its admin
error MaxLevelsReached();

/// @notice Emitted when an attempted hat id has empty intermediate level(s)
error InvalidHatId();

/// @notice Emitted when attempting to mint `hatId` to a `wearer` who is already wearing the hat
error AlreadyWearingHat(address wearer, uint256 hatId);

/// @notice Emitted when attempting to mint a non-existant hat
error HatDoesNotExist(uint256 hatId);

/// @notice Emmitted when attempting to mint or transfer a hat that is not active
error HatNotActive();

/// @notice Emitted when attempting to mint or transfer a hat to an ineligible wearer
error NotEligible();

Expand Down Expand Up @@ -66,6 +72,11 @@ interface HatsErrors {
/// @notice Emitted when attempting to link a tophat without a request
error LinkageNotRequested();

/// @notice Emmited when attempted to change a hat's eligibility or toggle module to the zero address
/// @notice Emmited when attempting to change a hat's eligibility or toggle module to the zero address
error ZeroAddress();

/// @notice Emmitted when attempting to change a hat's details or imageURI to a string with over 7000 bytes (~characters)
/// @dev This protects against a DOS attack where an admin iteratively extend's a hat's details or imageURI
/// to be so long that reading it exceeds the block gas limit, breaking `uri()` and `viewHat()`
error StringTooLong();
}
23 changes: 21 additions & 2 deletions src/Interfaces/IHats.sol
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,25 @@ interface IHats is IHatsIdUtilities, HatsErrors, HatsEvents {

function requestLinkTopHatToTree(uint32 _topHatId, uint256 _newAdminHat) external;

function approveLinkTopHatToTree(uint32 _topHatId, uint256 _newAdminHat) external;
function approveLinkTopHatToTree(
uint32 _topHatId,
uint256 _newAdminHat,
address _eligibility,
address _toggle,
string calldata _details,
string calldata _imageURI
) external;

function unlinkTopHatFromTree(uint32 _topHatId) external;

function relinkTopHatWithinTree(uint32 _topHatDomain, uint256 _newAdminHat) external;
function relinkTopHatWithinTree(
uint32 _topHatDomain,
uint256 _newAdminHat,
address _eligibility,
address _toggle,
string calldata _details,
string calldata _imageURI
) external;

/*//////////////////////////////////////////////////////////////
VIEW FUNCTIONS
Expand Down Expand Up @@ -122,5 +136,10 @@ interface IHats is IHatsIdUtilities, HatsErrors, HatsEvents {

function balanceOf(address wearer, uint256 hatId) external view returns (uint256 balance);

function balanceOfBatch(address[] calldata _wearers, uint256[] calldata _hatIds)
external
view
returns (uint256[] memory);

function uri(uint256 id) external view returns (string memory _uri);
}
2 changes: 2 additions & 0 deletions src/Interfaces/IHatsIdUtilities.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ interface IHatsIdUtilities {

function isLocalTopHat(uint256 _hatId) external pure returns (bool _localTopHat);

function isValidHatId(uint256 _hatId) external view returns (bool validHatId);

function getAdminAtLevel(uint256 _hatId, uint32 _level) external view returns (uint256 admin);

function getAdminAtLocalLevel(uint256 _hatId, uint32 _level) external pure returns (uint256 admin);
Expand Down
Loading

0 comments on commit 7509b70

Please sign in to comment.