Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

batch create hats #61

Merged
merged 5 commits into from
Oct 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ libs = ['lib']
optimizer_runs = 1000000
gas_reports = ["*"]
auto_detect_solc = false
solc = "0.8.15"
solc = "0.8.16"
remappings = [
"ds-test/=lib/forge-std/lib/ds-test/src/",
"forge-std/=lib/forge-std/src/",
Expand Down
2 changes: 1 addition & 1 deletion lib/openzeppelin-contracts
103 changes: 77 additions & 26 deletions src/Hats.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import "@openzeppelin/contracts/utils/Strings.sol";

/// @title Hats Protocol
/// @notice Hats are DAO-native revocable roles that are represented as semi-fungable tokens for composability
/// @dev This contract can manage all Hats for a given chain
/// @dev This is a multitenant contract that can manage all Hats for a given chain
/// @author Hats Protocol
contract Hats is ERC1155, HatsIdUtilities {
/*//////////////////////////////////////////////////////////////
Expand All @@ -36,6 +36,30 @@ contract Hats is ERC1155, HatsIdUtilities {
error SafeTransfersNotNecessary();
error MaxLevelsReached();

/*//////////////////////////////////////////////////////////////
HATS EVENTS
//////////////////////////////////////////////////////////////*/

event HatCreated(
uint256 id,
string details,
uint32 maxSupply,
address eligibility,
address toggle,
string imageURI
);

event HatRenounced(uint256 hatId, address wearer);

event WearerStatus(
uint256 hatId,
address wearer,
bool eligible,
bool wearerStanding
);

event HatStatusChanged(uint256 hatId, bool newStatus);

/*//////////////////////////////////////////////////////////////
HATS DATA MODELS
//////////////////////////////////////////////////////////////*/
Expand Down Expand Up @@ -73,29 +97,9 @@ contract Hats is ERC1155, HatsIdUtilities {
mapping(uint256 => mapping(address => bool)) public badStandings; // key: hatId => value: (key: wearer => value: badStanding?)

/*//////////////////////////////////////////////////////////////
HATS EVENTS
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/

event HatCreated(
uint256 id,
string details,
uint32 maxSupply,
address eligibility,
address toggle,
string imageURI
);

event HatRenounced(uint256 hatId, address wearer);

event WearerStatus(
uint256 hatId,
address wearer,
bool eligible,
bool wearerStanding
);

event HatStatusChanged(uint256 hatId, bool newStatus);

constructor(string memory _name, string memory _baseImageURI) {
name = _name;
baseImageURI = _baseImageURI;
Expand Down Expand Up @@ -180,15 +184,17 @@ contract Hats is ERC1155, HatsIdUtilities {
address _toggle,
string memory _imageURI
) public returns (uint256 newHatId) {
// to create a hat, you must be wearing the Hat of its admin
if (!isWearerOfHat(msg.sender, _admin)) {
revert NotAdmin(msg.sender, _admin);
}
if (uint8(_admin) > 0) {
revert MaxLevelsReached();
}

newHatId = getNextId(_admin);

// to create a hat, you must be wearing one of its admin hats
if (!isAdminOfHat(msg.sender, newHatId)) {
revert NotAdmin(msg.sender, newHatId);
}

// create the new hat
_createHat(
newHatId,
Expand All @@ -198,10 +204,55 @@ contract Hats is ERC1155, HatsIdUtilities {
_toggle,
_imageURI
);

// increment _admin.lastHatId
++_hats[_admin].lastHatId;
}

/// @notice Creates new hats in batch. The msg.sender must be an admin of each hat.
/// @dev This is a convenience function that loops through the arrays and calls `createHat`.
/// @param _admins Array of ids of admins for each hat to create
/// @param _details Array of details for each hat to create
/// @param _maxSupplies Array of supply caps for each hat to create
/// @param _eligibilityModules Array of eligibility module addresses for each hat to
/// create
/// @param _toggleModules Array of toggle module addresses for each hat to create
/// @param _imageURIs Array of imageURIs for each hat to create
/// @return bool True if all createHat calls succeeded
function batchCreateHats(
uint256[] memory _admins,
string[] memory _details,
uint32[] memory _maxSupplies,
address[] memory _eligibilityModules,
address[] memory _toggleModules,
string[] memory _imageURIs
) public returns (bool) {
// check if array lengths are the same
uint256 length = _admins.length; // save an MLOAD

bool sameLengths = (length == _details.length &&
length == _maxSupplies.length &&
length == _eligibilityModules.length &&
length == _toggleModules.length &&
length == _imageURIs.length);

if (!sameLengths) revert BatchArrayLengthMismatch();

// loop through and create each hat
for (uint256 i = 0; i < length; ++i) {
createHat(
_admins[i],
_details[i],
_maxSupplies[i],
_eligibilityModules[i],
_toggleModules[i],
_imageURIs[i]
);
}

return true;
}

function getNextId(uint256 _admin) public view returns (uint256) {
uint8 nextHatId = _hats[_admin].lastHatId + 1;
return buildHatId(_admin, nextHatId);
Expand Down
11 changes: 7 additions & 4 deletions src/HatsIdUtilities.sol
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ contract HatsIdUtilities {
/// @dev Check hats[_admin].lastHatId for the previous hat created underneath _admin
/// @param _admin the id of the admin for the new hat
/// @param _newHat the uint8 id of the new hat
/// @return uint256 the constructed hat id
/// @return id The constructed hat id
function buildHatId(uint256 _admin, uint8 _newHat)
public
pure
returns (uint256)
returns (uint256 id)
{
uint256 mask;
for (uint256 i = 0; i < MAX_LEVELS; ++i) {
Expand All @@ -43,18 +43,19 @@ contract HatsIdUtilities {
(TOPHAT_ADDRESS_SPACE + (LOWER_LEVEL_ADDRESS_SPACE * i))
);
if (_admin & mask == 0) {
return
id =
_admin |
(uint256(_newHat) <<
(LOWER_LEVEL_ADDRESS_SPACE * (MAX_LEVELS - 1 - i)));
return id;
}
}
}

/// @notice Identifies the level a given hat in its hat tree
/// @param _hatId the id of the hat in question
/// @return level (0 to 28)
function getHatLevel(uint256 _hatId) public pure returns (uint8 level) {
function getHatLevel(uint256 _hatId) public pure returns (uint8) {
uint256 mask;
uint256 i;
for (i = 0; i < MAX_LEVELS; ++i) {
Expand All @@ -65,6 +66,8 @@ contract HatsIdUtilities {

if (_hatId & mask == 0) return uint8(i);
}

return uint8(MAX_LEVELS);
}

/// @notice Checks whether a hat is a topHat
Expand Down
Loading