-
Notifications
You must be signed in to change notification settings - Fork 153
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(HatsGatekeepers): add Hats gatekeeper contracts
Adds two flavors of Hats Protocol-powered gatekeeper contracts: - `*Single` gates registration to wearers of a single hat - `*Multiple` gates registration to wearers of multiple hats.
- Loading branch information
Showing
4 changed files
with
515 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.10; | ||
|
||
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; | ||
|
||
import { SignUpGatekeeper } from "./SignUpGatekeeper.sol"; | ||
|
||
interface IHats { | ||
function isWearerOfHat(address account, uint256 hat) external view returns (bool); | ||
} | ||
|
||
// custom errors | ||
error OnlyMACI(); | ||
error NotWearingCriterionHat(); | ||
error NotCriterionHat(); | ||
error AlreadyRegistered(); | ||
|
||
contract HatsGatekeeperSingle is SignUpGatekeeper, Ownable { | ||
/// @notice The Hats Protocol contract address | ||
IHats public immutable hats; | ||
|
||
/// @notice The hat that users must wear to be eligible to register | ||
uint256 public immutable criterionHat; | ||
|
||
/// @notice the reference to the MACI contract | ||
address public maci; | ||
|
||
/// @notice Tracks registered users | ||
mapping(address => bool) public registered; | ||
|
||
/// @notice Deploy an instance of HatsGatekeeper | ||
/// @param _hats The Hats Protocol contract | ||
/// @param _criterionHat The required hat // could also accept multiple hats in an array | ||
constructor(address _hats, uint256 _criterionHat) payable { | ||
hats = IHats(_hats); | ||
criterionHat = _criterionHat; | ||
} | ||
|
||
/*////////////////////////////////////////////////////////////// | ||
OWNER FUNCTIONS | ||
//////////////////////////////////////////////////////////////*/ | ||
|
||
/// @notice Allows to set the MACI contract | ||
function setMaciInstance(address _maci) public override onlyOwner { | ||
maci = _maci; | ||
} | ||
|
||
/*////////////////////////////////////////////////////////////// | ||
GATEKEEPER FUNCTION | ||
//////////////////////////////////////////////////////////////*/ | ||
|
||
/// @notice Registers the user | ||
/// @param _user The address of the user | ||
function register(address _user, bytes memory /*_data*/) public override { | ||
// ensure that the caller is the MACI contract | ||
if (maci != msg.sender) revert OnlyMACI(); | ||
|
||
// _user must not be already registered | ||
if (registered[_user]) revert AlreadyRegistered(); | ||
|
||
registered[_user] = true; | ||
|
||
// _user must be wearing the criterion hat | ||
if (!hats.isWearerOfHat(_user, criterionHat)) revert NotWearingCriterionHat(); | ||
} | ||
} | ||
|
||
contract HatsGatekeeperMultiple is SignUpGatekeeper, Ownable { | ||
/// @notice The Hats Protocol contract address | ||
IHats public immutable hats; | ||
|
||
/// @notice Tracks hats that users must wear to be eligible to register | ||
mapping(uint256 => bool) public criterionHat; | ||
|
||
/// @notice the reference to the MACI contract | ||
address public maci; | ||
|
||
/// @notice Tracks registered users | ||
mapping(address => bool) public registered; | ||
|
||
/// @notice Deploy an instance of HatsGatekeeper | ||
/// @param _hats The Hats Protocol contract | ||
/// @param _criterionHats Array of accepted criterion hats | ||
constructor(address _hats, uint256[] memory _criterionHats) payable { | ||
hats = IHats(_hats); | ||
|
||
// add the criterion hats | ||
for (uint256 i; i < _criterionHats.length; ++i) { | ||
criterionHat[_criterionHats[i]] = true; | ||
} | ||
} | ||
|
||
/*////////////////////////////////////////////////////////////// | ||
OWNER FUNCTIONS | ||
//////////////////////////////////////////////////////////////*/ | ||
|
||
/// @notice Allows to set the MACI contract | ||
function setMaciInstance(address _maci) public override onlyOwner { | ||
maci = _maci; | ||
} | ||
|
||
/*////////////////////////////////////////////////////////////// | ||
GATEKEEPER FUNCTION | ||
//////////////////////////////////////////////////////////////*/ | ||
|
||
/// @notice Registers the user | ||
/// @param _user The address of the user | ||
/// @param _data additional data | ||
function register(address _user, bytes memory _data) public override { | ||
// ensure that the caller is the MACI contract | ||
if (maci != msg.sender) revert OnlyMACI(); | ||
|
||
// _user must not be already registered | ||
if (registered[_user]) revert AlreadyRegistered(); | ||
|
||
// decode the _data as a hat | ||
uint256 hat = abi.decode(_data, (uint256)); | ||
|
||
// the hat must be set as a criterion hat | ||
if (!criterionHat[hat]) revert NotCriterionHat(); | ||
|
||
registered[_user] = true; | ||
|
||
// _user must be wearing the criterion hat | ||
if (!hats.isWearerOfHat(_user, hat)) revert NotWearingCriterionHat(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.10; | ||
|
||
/// @title IHats | ||
/// @notice The interface for the Hats Protocol contract | ||
interface IHats { | ||
function mintTopHat(address _target, string calldata _details, string calldata _imageURI) external returns (uint256); | ||
function createHat( | ||
uint256 _admin, | ||
string calldata _details, | ||
uint32 _maxSupply, | ||
address _eligibility, | ||
address _toggle, | ||
bool _mutable, | ||
string calldata _imageURI | ||
) external returns (uint256); | ||
function mintHat(uint256 _hatId, address _wearer) external returns (bool success); | ||
} | ||
|
||
/// @title MockHatsProtocol | ||
/// @notice A mock contract to test the HatsGatekeeper | ||
contract MockHatsProtocol { | ||
IHats public immutable hats; | ||
|
||
uint256 public lastTopHat; | ||
uint256 public lastHat; | ||
|
||
/// @notice Deploy an instance of MockHatsProtocol | ||
/// @param _hats The Hats Protocol contract | ||
constructor(address _hats) payable { | ||
hats = IHats(_hats); | ||
} | ||
|
||
/// @notice Creates and mints a Hat that is its own admin, i.e. a "topHat" | ||
/// @dev A topHat has no eligibility and no toggle | ||
/// @param _target The address to which the newly created topHat is minted | ||
/// @param _details A description of the Hat [optional]. Should not be larger than 7000 bytes | ||
/// (enforced in changeHatDetails) | ||
/// @param _imageURI The image uri for this top hat and the fallback for its | ||
/// downstream hats [optional]. Should not be large than 7000 bytes | ||
/// (enforced in changeHatImageURI) | ||
function mintTopHat(address _target, string calldata _details, string calldata _imageURI) external { | ||
lastTopHat = hats.mintTopHat(_target, _details, _imageURI); | ||
} | ||
|
||
/// @notice Creates a new hat. The msg.sender must wear the `_admin` hat. | ||
/// @dev Initializes a new Hat struct, but does not mint any tokens. | ||
/// @param _details A description of the Hat. Should not be larger than 7000 bytes (enforced in changeHatDetails) | ||
/// @param _maxSupply The total instances of the Hat that can be worn at once | ||
/// @param _admin The id of the Hat that will control who wears the newly created hat | ||
/// @param _eligibility The address that can report on the Hat wearer's status | ||
/// @param _toggle The address that can deactivate the Hat | ||
/// @param _mutable Whether the hat's properties are changeable after creation | ||
/// @param _imageURI The image uri for this hat and the fallback for its | ||
/// downstream hats [optional]. Should not be larger than 7000 bytes (enforced in changeHatImageURI) | ||
function createHat( | ||
uint256 _admin, | ||
string calldata _details, | ||
uint32 _maxSupply, | ||
address _eligibility, | ||
address _toggle, | ||
bool _mutable, | ||
string calldata _imageURI | ||
) external { | ||
lastHat = hats.createHat(_admin, _details, _maxSupply, _eligibility, _toggle, _mutable, _imageURI); | ||
} | ||
|
||
/// @notice Mints a hat to the specified wearer | ||
/// @param _hatId The id of the hat to mint | ||
/// @param _wearer The address of the wearer | ||
function mintHat(uint256 _hatId, address _wearer) external { | ||
hats.mintHat(_hatId, _wearer); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.