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

feat(contracts): add hats gatekeeper contracts #1191

Merged
merged 8 commits into from
Mar 18, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

Check warning

Code scanning / Slither

Incorrect versions of Solidity Warning

Pragma version^0.8.10 allows old versions

import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { SignUpGatekeeper } from "../SignUpGatekeeper.sol";
import { IHats } from "../../interfaces/IHats.sol";

/// @title HatsGatekeeperBase
/// @notice Abastract contract containing the base elements of a Hats Gatekeeper contract
abstract contract HatsGatekeeperBase is SignUpGatekeeper, Ownable {
0xmad marked this conversation as resolved.
Show resolved Hide resolved
/*//////////////////////////////////////////////////////////////
CUSTOM ERRORS
//////////////////////////////////////////////////////////////*/

error OnlyMACI();
error NotWearingCriterionHat();
error AlreadyRegistered();
error ZeroAddress();

/*//////////////////////////////////////////////////////////////
PUBLIC CONSTANTS
//////////////////////////////////////////////////////////////*/

/// @notice The Hats Protocol contract address
IHats public immutable hats;

/*//////////////////////////////////////////////////////////////
PUBLIC STATE VARIABLES
//////////////////////////////////////////////////////////////*/

/// @notice the reference to the MACI contract
address public maci;

/// @notice Tracks registered users
mapping(address => bool) public registered;

/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/

/// @notice Deploy an instance of HatsGatekeeper
/// @param _hats The Hats Protocol contract
constructor(address _hats) payable {
hats = IHats(_hats);
}

/*//////////////////////////////////////////////////////////////
OWNER FUNCTIONS
//////////////////////////////////////////////////////////////*/

/// @notice Allows to set the MACI contract
/// @param _maci The MACI contract interface to be stored
function setMaciInstance(address _maci) public override onlyOwner {

Check warning

Code scanning / Slither

Conformance to Solidity naming conventions Warning

if (_maci == address(0)) revert ZeroAddress();
maci = _maci;
spengrah marked this conversation as resolved.
Show resolved Hide resolved
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

Check warning

Code scanning / Slither

Incorrect versions of Solidity Warning

Pragma version^0.8.10 allows old versions

import { HatsGatekeeperBase } from "./HatsGatekeeperBase.sol";

/// @title HatsGatekeeperMultiple
/// @notice A gatekeeper contract which allows users to sign up to MACI
/// only if they are wearing one of the specified hats
contract HatsGatekeeperMultiple is HatsGatekeeperBase {
/*//////////////////////////////////////////////////////////////
CUSTOM ERRORS
//////////////////////////////////////////////////////////////*/

error NotCriterionHat();

/*//////////////////////////////////////////////////////////////
PUBLIC CONSTANTS
//////////////////////////////////////////////////////////////*/

/// @notice Tracks hats that users must wear to be eligible to register
mapping(uint256 => bool) public criterionHat;

/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/

/// @notice Deploy an instance of HatsGatekeeperMultiple
/// @param _hats The Hats Protocol contract
/// @param _criterionHats Array of accepted criterion hats
constructor(address _hats, uint256[] memory _criterionHats) payable HatsGatekeeperBase(_hats) {
// add the criterion hats
for (uint256 i; i < _criterionHats.length; ++i) {
criterionHat[_criterionHats[i]] = true;
}
}

/*//////////////////////////////////////////////////////////////
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 {

Check warning

Code scanning / Slither

Conformance to Solidity naming conventions Warning

Check warning

Code scanning / Slither

Conformance to Solidity naming conventions Warning

// 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();
}
}

Check warning

Code scanning / Slither

Contracts that lock Ether Medium

Contract locking ether found:
Contract HatsGatekeeperMultiple has payable functions:
- HatsGatekeeperBase.constructor(address)
- HatsGatekeeperMultiple.constructor(address,uint256[])
But does not have a function to withdraw the ether
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

Check warning

Code scanning / Slither

Incorrect versions of Solidity Warning

Pragma version^0.8.10 allows old versions

import { HatsGatekeeperBase } from "./HatsGatekeeperBase.sol";

/// @title HatsGatekeeperSingle
/// @notice A gatekeeper contract which allows users to sign up to MACI
/// only if they are wearing a specified hat
contract HatsGatekeeperSingle is HatsGatekeeperBase {
/*//////////////////////////////////////////////////////////////
PUBLIC CONSTANTS
//////////////////////////////////////////////////////////////*/

/// @notice The hat that users must wear to be eligible to register
uint256 public immutable criterionHat;

/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/

/// @notice Deploy an instance of HatsGatekeeperSingle
/// @param _hats The Hats Protocol contract
/// @param _criterionHat The required hat
constructor(address _hats, uint256 _criterionHat) payable HatsGatekeeperBase(_hats) {
criterionHat = _criterionHat;
}

/*//////////////////////////////////////////////////////////////
GATEKEEPER FUNCTION
//////////////////////////////////////////////////////////////*/

/// @notice Registers the user
/// @param _user The address of the user
function register(address _user, bytes memory /*_data*/) public override {

Check warning

Code scanning / Slither

Conformance to Solidity naming conventions Warning

// 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();
}
}

Check warning

Code scanning / Slither

Contracts that lock Ether Medium

Contract locking ether found:
Contract HatsGatekeeperSingle has payable functions:
- HatsGatekeeperBase.constructor(address)
- HatsGatekeeperSingle.constructor(address,uint256)
But does not have a function to withdraw the ether
53 changes: 53 additions & 0 deletions contracts/contracts/interfaces/IHats.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

Check warning

Code scanning / Slither

Incorrect versions of Solidity Warning

Pragma version^0.8.10 allows old versions

/// @title IHats
/// @notice Minimal interface for the Hats Protocol contract
/// @dev Includes only the functions required for the HatsGatekeepers and associated tests
interface IHats {
/// @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 larger than 7000 bytes
/// (enforced in changeHatImageURI)
/// @return topHatId The id of the newly created topHat
function mintTopHat(address _target, string calldata _details, string calldata _imageURI) external returns (uint256);

/// @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)
/// @return newHatId The id of the newly created Hat
function createHat(
uint256 _admin,
string calldata _details,
uint32 _maxSupply,
address _eligibility,
address _toggle,
bool _mutable,
string calldata _imageURI
) external returns (uint256);

/// @notice Mints an ERC1155-similar token of the Hat to an eligible recipient, who then "wears" the hat
/// @dev The msg.sender must wear an admin Hat of `_hatId`, and the recipient must be eligible to wear `_hatId`
/// @param _hatId The id of the Hat to mint
/// @param _wearer The address to which the Hat is minted
/// @return success Whether the mint succeeded
function mintHat(uint256 _hatId, address _wearer) external returns (bool success);

/// @notice Checks whether a given address wears a given Hat
/// @dev Convenience function that wraps `balanceOf`
/// @param account The address in question
/// @param hat The id of the Hat that the `_user` might wear
/// @return isWearer Whether the `_user` wears the Hat.
function isWearerOfHat(address account, uint256 hat) external view returns (bool);
}
49 changes: 49 additions & 0 deletions contracts/contracts/mocks/MockHatsProtocol.sol
spengrah marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

Check warning

Code scanning / Slither

Incorrect versions of Solidity Warning

Pragma version^0.8.10 allows old versions

import { IHats } from "../interfaces/IHats.sol";

/// @title MockHatsProtocol
/// @notice A mock contract to test the HatsGatekeeper
contract MockHatsProtocol is IHats {
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);
}

/// @inheritdoc IHats
function mintTopHat(address _target, string calldata _details, string calldata _imageURI) external returns (uint256) {

Check warning

Code scanning / Slither

Conformance to Solidity naming conventions Warning

Check warning

Code scanning / Slither

Conformance to Solidity naming conventions Warning

Check warning

Code scanning / Slither

Conformance to Solidity naming conventions Warning

lastTopHat = hats.mintTopHat(_target, _details, _imageURI);
return lastTopHat;
}

/// @inheritdoc IHats
function createHat(
uint256 _admin,

Check warning

Code scanning / Slither

Conformance to Solidity naming conventions Warning

string calldata _details,

Check warning

Code scanning / Slither

Conformance to Solidity naming conventions Warning

uint32 _maxSupply,

Check warning

Code scanning / Slither

Conformance to Solidity naming conventions Warning

address _eligibility,

Check warning

Code scanning / Slither

Conformance to Solidity naming conventions Warning

address _toggle,

Check warning

Code scanning / Slither

Conformance to Solidity naming conventions Warning

bool _mutable,

Check warning

Code scanning / Slither

Conformance to Solidity naming conventions Warning

string calldata _imageURI

Check warning

Code scanning / Slither

Conformance to Solidity naming conventions Warning

) external returns (uint256) {
lastHat = hats.createHat(_admin, _details, _maxSupply, _eligibility, _toggle, _mutable, _imageURI);
return lastHat;
}

/// @inheritdoc IHats
function mintHat(uint256 _hatId, address _wearer) external returns (bool) {

Check warning

Code scanning / Slither

Conformance to Solidity naming conventions Warning

Parameter MockHatsProtocol.mintHat(uint256,address)._hatId is not in mixedCase

Check warning

Code scanning / Slither

Conformance to Solidity naming conventions Warning

return hats.mintHat(_hatId, _wearer);
}

/// @inheritdoc IHats
function isWearerOfHat(address account, uint256 hat) external view override returns (bool) {
return hats.isWearerOfHat(account, hat);
}
}

Check warning

Code scanning / Slither

Contracts that lock Ether Medium

Contract locking ether found:
Contract MockHatsProtocol has payable functions:
- MockHatsProtocol.constructor(address)
But does not have a function to withdraw the ether
1 change: 1 addition & 0 deletions contracts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"test:pollFactory": "pnpm run test ./tests/PollFactory.test.ts",
"test:subsidy": "pnpm run test ./tests/Subsidy.test.ts",
"test:eas_gatekeeper": "pnpm run test ./tests/EASGatekeeper.test.ts",
"test:hats_gatekeeper": "pnpm run test ./tests/HatsGatekeeper.test.ts",
"deploy": "hardhat deploy-full",
"deploy-poll": "hardhat deploy-poll",
"verify": "hardhat verify-full",
Expand Down
Loading
Loading