# Hats Research II
## Linkable Trees Branch
### SampleMultiHatter.sol
```
pragma solidity >=0.8.13;
import "../HatsEligibility/IHatsEligibility.sol";
import "../Interfaces/IHats.sol";
import "utils/Auth.sol";
/// @notice designed to serve as the admin for multiple hats
abstract contract SampleMultiHatter is Auth {
IHats public HATS;
constructor(address _hatsContract) {
HATS = IHats(_hatsContract);
}
// DAO governance mint function (auth)
function mint(uint256 _hatId, address _wearer) public virtual requiresAuth {
_mint(_hatId, _wearer);
}
function _mint(uint256 _hatId, address _wearer) internal virtual {
require(HATS.isInGoodStanding(_wearer, _hatId), "not eligible");
HATS.mintHat(_hatId, _wearer);
}
function createAndMint(
uint256 _admin,
string memory _details,
uint32 _maxSupply,
address _eligibility,
address _toggle,
address _wearer,
bool _mutable,
string memory _imageURI
) public virtual requiresAuth {
uint256 id = HATS.createHat(
_admin,
_details,
_maxSupply,
_eligibility,
_toggle,
_mutable,
_imageURI
);
_mint(id, _wearer);
}
// DAO governance transfer function (auth)
function transfer(
uint256 _hatId,
address _wearer,
address _newWearer
) public virtual requiresAuth {
HATS.transferHat(_hatId, _wearer, _newWearer);
}
}
```
---
This is a Solidity contract for a "MultiHatter", which appears to be an abstract contract (a contract that cannot be directly instantiated) that serves as an administrator for multiple "hats". The contract uses the "Auth" contract (presumably for authentication purposes) and imports three other contracts: "IHatsEligibility", "IHats", and "utils/Auth.sol".
The contract has a single public variable, "HATS", which is an instance of the "IHats" contract. The contract has a constructor that takes an address as an argument and assigns this address to the "HATS" variable as an instance of the "IHats" contract.
The contract has two functions for minting new hats: "mint" and "createAndMint". The "mint" function takes an unsigned integer "_hatId" and an address "_wearer" as arguments and calls an internal function "_mint" with these arguments. The "_mint" function checks if the wearer is eligible to receive the hat (using the "isInGoodStanding" function from the "IHatsEligibility" contract) and, if so, calls the "mintHat" function from the "IHats" contract to mint the hat for the wearer.
The "createAndMint" function allows the creation of a new hat and minting it to a wearer in a single transaction. It takes several arguments: an unsigned integer "_admin" (presumably the address of the hat's administrator), a string "_details" (presumably details about the hat), an unsigned integer "_maxSupply" (the maximum number of hats that can be minted), an address "_eligibility" (the contract that determines hat eligibility), an address "_toggle" (the contract that can enable or disable the hat), an address "_wearer" (the intended wearer of the new hat), a boolean "_mutable" (presumably indicating whether the hat is mutable or not), and a string "_imageURI" (presumably the URI of an image for the hat). This function first calls the "createHat" function from the "IHats" contract with these arguments to create the new hat, and then calls the "_mint" function to mint the new hat to the wearer.
The contract also has a function for transferring a hat from one wearer to another: "transfer". This function takes an unsigned integer "_hatId" (the ID of the hat to be transferred), an address "_wearer" (the current wearer of the hat), and an address "_newWearer" (the intended new wearer of the hat) as arguments and calls the "transferHat" function from the "IHats" contract with these arguments.
I hope this helps! Let me know if you have any questions.
---
### iHatsEligibity.sol (Interface)
```
pragma solidity >=0.8.13;
interface IHatsEligibility {
/// @notice Returns the status of a wearer for a given hat
/// @dev If standing is false, eligibility MUST also be false
/// @param _wearer The address of the current or prospective Hat wearer
/// @param _hatId The id of the hat in question
/// @return eligible Whether the _wearer is eligible to wear the hat
/// @return standing Whether the _wearer is in goog standing
function getWearerStatus(address _wearer, uint256 _hatId)
external
view
returns (bool eligible, bool standing);
}
```
This is the interface for a Solidity contract called "IHatsEligibility". An interface in Solidity is a contract that defines a set of functions that other contracts can implement. In this case, the "IHatsEligibility" interface defines a single function, "getWearerStatus", which other contracts can implement in order to provide a way to check the eligibility and standing of a wearer for a given hat.
The "getWearerStatus" function takes an address "_wearer" and an unsigned integer "_hatId" as arguments and returns a tuple containing two boolean values: "eligible" and "standing". The "eligible" value indicates whether the wearer is eligible to wear the hat, and the "standing" value indicates whether the wearer is in good standing. The function is marked as "view" and "external", which means that it is a read-only function that can be called from other contracts or from outside the blockchain.
I hope this helps! Let me know if you have any questions.
---
### OwnableHatsEligibility.sol
```
pragma solidity >=0.8.13;
import "./IHatsEligibility.sol";
import "../Interfaces/IHats.sol";
import "utils/Auth.sol";
abstract contract OwnableHatsEligibility is IHats, IHatsEligibility, Auth {
event HatStandingSet(
address _wearer,
uint256 _hatId,
bool _eligible,
bool _standing
);
IHats public HATS;
mapping(address => mapping(uint256 => bool)) public standings;
constructor(address _hatsContract) {
HATS = IHats(_hatsContract);
}
function getWearerStatus(address _wearer, uint64 _hatId)
public
view
returns (bool)
{
return standings[_wearer][_hatId];
}
function setWearerStatus(
address _wearer,
uint256 _hatId,
bool _eligible,
bool _standing
) public virtual requiresAuth {
standings[_wearer][_hatId] = _standing;
_updateHatWearerStatus(_wearer, _hatId, _eligible, _standing);
emit HatStandingSet(_wearer, _hatId, _eligible, _standing);
}
function _updateHatWearerStatus(
address _wearer,
uint256 _hatId,
bool _eligible,
bool _standing
) internal virtual {
HATS.setHatWearerStatus(_hatId, _wearer, _eligible, _standing);
}
}
```
This is a Solidity contract for an "OwnableHatsEligibility" contract, which appears to be an abstract contract (a contract that cannot be directly instantiated) that provides a way to check and set the eligibility and standing of a wearer for a given hat. The contract uses three other contracts: "IHatsEligibility", "IHats", and "utils/Auth.sol".
The contract has a single public variable, "HATS", which is an instance of the "IHats" contract. The contract has a constructor that takes an address as an argument and assigns this address to the "HATS" variable as an instance of the "IHats" contract.
The contract has a public event called "HatStandingSet", which is triggered when the standing of a hat wearer is set. The event has four arguments: an address "_wearer" (the address of the wearer), an unsigned integer "_hatId" (the ID of the hat), a boolean "_eligible" (indicating whether the wearer is eligible to wear the hat), and a boolean "_standing" (indicating whether the wearer is in good standing).
The contract has a public mapping called "standings", which maps addresses to mappings of hat IDs to boolean values. This mapping is used to store the standing of each hat wearer.
The contract has two functions for checking and setting the standing of hat wearers: "getWearerStatus" and "setWearerStatus". The "getWearerStatus" function takes an address "_wearer" and an unsigned integer "_hatId" as arguments and returns the standing of the wearer for the given hat as a boolean value. The "setWearerStatus" function takes four arguments: an address "_wearer" (the address of the wearer), an unsigned integer "_hatId" (the ID of the hat), a boolean "_eligible" (indicating whether the wearer is eligible to wear the hat), and a boolean "_standing" (indicating whether the wearer is in good standing). This function updates the standing of the wearer for the given hat in the "standings" mapping and calls an internal function "_updateHatWearerStatus" with these arguments. The "_updateHatWearerStatus" function is an abstract function that other contracts can implement to specify how to update the standing of a hat wearer.
---
### iHat.sol interface
```
pragma solidity >=0.8.13;
import "./IHatsIdUtilities.sol";
import "./HatsErrors.sol";
import "./HatsEvents.sol";
interface IHats is IHatsIdUtilities, HatsErrors, HatsEvents {
function mintTopHat(
address _target,
string memory _details,
string memory _imageURI
) external returns (uint256 topHatId);
// function createTopHatAndHat(
// string memory _details,
// uint32 _maxSupply,
// address _eligibility,
// address _toggle,
// bool _mutable,
// string memory _topHatImageURI,
// string memory _firstHatImageURI
// ) external returns (uint256 topHatId, uint256 firstHatId);
function createHat(
uint256 _admin,
string memory _details,
uint32 _maxSupply,
address _eligibility,
address _toggle,
bool _mutable,
string memory _imageURI
) external returns (uint256 newHatId);
function batchCreateHats(
uint256[] memory _admins,
string[] memory _details,
uint32[] memory _maxSupplies,
address[] memory _eligibilityModules,
address[] memory _toggleModules,
bool[] memory _mutables,
string[] memory _imageURIs
) external returns (bool);
function getNextId(uint256 _admin) external view returns (uint256);
function mintHat(uint256 _hatId, address _wearer) external returns (bool);
function batchMintHats(uint256[] memory _hatIds, address[] memory _wearers)
external
returns (bool);
function setHatStatus(uint256 _hatId, bool _newStatus)
external
returns (bool);
function checkHatStatus(uint256 _hatId) external returns (bool);
function setHatWearerStatus(
uint256 _hatId,
address _wearer,
bool _eligible,
bool _standing
) external returns (bool);
function checkHatWearerStatus(uint256 _hatId, address _wearer)
external
returns (bool);
function renounceHat(uint256 _hatId) external;
function transferHat(
uint256 _hatId,
address _from,
address _to
) external;
/*//////////////////////////////////////////////////////////////
HATS ADMIN FUNCTIONS
//////////////////////////////////////////////////////////////*/
function makeHatImmutable(uint256 _hatId) external;
function changeHatDetails(uint256 _hatId, string memory _newDetails)
external;
function changeHatEligibility(uint256 _hatId, address _newEligibility)
external;
function changeHatToggle(uint256 _hatId, address _newToggle) external;
function changeHatImageURI(uint256 _hatId, string memory _newImageURI)
external;
function changeHatMaxSupply(uint256 _hatId, uint32 _newMaxSupply) external;
/*//////////////////////////////////////////////////////////////
VIEW FUNCTIONS
//////////////////////////////////////////////////////////////*/
function viewHat(uint256 _hatId)
external
view
returns (
string memory details,
uint32 maxSupply,
uint32 supply,
address eligibility,
address toggle,
string memory imageURI,
uint16 lastHatId,
bool mutable_,
bool active
);
function isWearerOfHat(address _user, uint256 _hatId)
external
view
returns (bool);
function isAdminOfHat(address _user, uint256 _hatId)
external
view
returns (bool);
// function isActive(uint256 _hatId) external view returns (bool);
function isInGoodStanding(address _wearer, uint256 _hatId)
external
view
returns (bool);
function isEligible(address _wearer, uint256 _hatId)
external
view
returns (bool);
function getImageURIForHat(uint256 _hatId)
external
view
returns (string memory);
function balanceOf(address wearer, uint256 hatId)
external
view
returns (uint256 balance);
function uri(uint256 id) external view returns (string memory);
}
```
This is the interface for a Solidity contract called "IHats". An interface in Solidity is a contract that defines a set of functions that other contracts can implement. In this case, the "IHats" interface defines a number of functions that other contracts can implement in order to provide a way to mint, create, and manage "hats" on the blockchain.
The contract imports three other contracts: "IHatsIdUtilities", "HatsErrors", and "HatsEvents". It appears that the "IHatsIdUtilities" contract provides utility functions related to hat IDs, the "HatsErrors" contract defines error messages, and the "HatsEvents" contract defines events that can be emitted by the "IHats" contract.
The "IHats" contract defines several functions for minting, creating, and managing hats:
"mintTopHat" allows the minting of a special "top hat" with a given set of details and an image URI.
"createHat" allows the creation of a new hat with a given set of details, including the hat's administrator, maximum supply, eligibility, toggle, mutability, and image URI.
"batchCreateHats" allows the creation of multiple hats in a single transaction.
"mintHat" allows the minting of an existing hat to a given wearer.
"batchMintHats" allows the minting of multiple hats in a single transaction.
"setHatStatus" allows the setting of the active status of a hat.
"checkHatStatus" allows the checking of the active status of a hat.
"setHatWearerStatus" allows the setting of the eligibility and standing of a hat wearer.
"checkHatWearerStatus" allows the checking of the eligibility and standing of a hat wearer.
"renounceHat" allows the renouncing of ownership of a hat.
"transferHat" allows the transfer of ownership of a hat from one wearer to another.
The "IHats" contract also defines several functions that can be called by the administrator of a hat to modify the hat's details and behavior:
"makeHatImmutable" allows the administrator to make a hat immutable (i.e., unable to be modified).
"changeHatDetails" allows the administrator to change the details of a hat.
"changeHatEligibility" allows the administrator to change the contract that determines the eligibility of hat wearers.
"changeHatToggle" allows the administrator to change the contract that can enable or disable the hat.
"changeHatImageURI" allows the administrator to change the image URI of a hat.
"changeHatMaxSupply" allows the administrator to change the maximum number of hats that can be minted.
The "IHats" contract also defines several "view" functions, which are read-only functions that do not modify the state of the contract:
"viewHat" allows the viewing of the details of a hat, including its details, maximum supply, current supply, eligibility, toggle, image URI, last hat ID, mutability, and active status.
"isWearerOfHat" allows the checking of whether a given address is the current wearer of a hat.
"isAdminOfHat" allows the checking of whether a given address is the administrator of a hat.
"isInGoodStanding" allows the checking of whether a given address is in good standing as the wearer of a hat.
"isEligible" allows the checking of whether a given address is eligible to wear a hat.
"getImageURIForHat" allows the retrieval of the image URI for a given hat.
"getWearers" allows the retrieval of the current wearers of a given hat.
---
### SampleHatter.sol
```
pragma solidity >=0.8.13;
import "../HatsEligibility/IHatsEligibility.sol";
import "../Interfaces/IHats.sol";
import "utils/Auth.sol";
/// @notice designed to serve as the admin for multiple hats
abstract contract SampleMultiHatter is Auth {
IHats public HATS;
constructor(address _hatsContract) {
HATS = IHats(_hatsContract);
}
// DAO governance mint function (auth)
function mint(uint256 _hatId, address _wearer) public virtual requiresAuth {
_mint(_hatId, _wearer);
}
function _mint(uint256 _hatId, address _wearer) internal virtual {
require(HATS.isInGoodStanding(_wearer, _hatId), "not eligible");
HATS.mintHat(_hatId, _wearer);
}
function createAndMint(
uint256 _admin,
string memory _details,
uint32 _maxSupply,
address _eligibility,
address _toggle,
address _wearer,
bool _mutable,
string memory _imageURI
) public virtual requiresAuth {
uint256 id = HATS.createHat(
_admin,
_details,
_maxSupply,
_eligibility,
_toggle,
_mutable,
_imageURI
);
_mint(id, _wearer);
}
// DAO governance transfer function (auth)
function transfer(
uint256 _hatId,
address _wearer,
address _newWearer
) public virtual requiresAuth {
HATS.transferHat(_hatId, _wearer, _newWearer);
}
}
```
### Auth.sol
```
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
abstract contract Auth {
event OwnerUpdated(address indexed user, address indexed newOwner);
event AuthorityUpdated(
address indexed user,
Authority indexed newAuthority
);
address public owner;
Authority public authority;
constructor(address _owner, Authority _authority) {
owner = _owner;
authority = _authority;
emit OwnerUpdated(msg.sender, _owner);
emit AuthorityUpdated(msg.sender, _authority);
}
modifier requiresAuth() virtual {
require(isAuthorized(msg.sender, msg.sig), "UNAUTHORIZED");
_;
}
function isAuthorized(address user, bytes4 functionSig)
internal
view
virtual
returns (bool)
{
Authority auth = authority; // Memoizing authority saves us a warm SLOAD, around 100 gas.
// Checking if the caller is the owner only after calling the authority saves gas in most cases, but be
// aware that this makes protected functions uncallable even to the owner if the authority is out of order.
return
(address(auth) != address(0) &&
auth.canCall(user, address(this), functionSig)) ||
user == owner;
}
function setAuthority(Authority newAuthority) public virtual {
// We check if the caller is the owner first because we want to ensure they can
// always swap out the authority even if it's reverting or using up a lot of gas.
require(
msg.sender == owner ||
authority.canCall(msg.sender, address(this), msg.sig)
);
authority = newAuthority;
emit AuthorityUpdated(msg.sender, newAuthority);
}
function setOwner(address newOwner) public virtual requiresAuth {
owner = newOwner;
emit OwnerUpdated(msg.sender, newOwner);
}
}
/// @notice A generic interface for a contract which provides authorization data to an Auth instance.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
interface Authority {
function canCall(
address user,
address target,
bytes4 functionSig
) external view returns (bool);
}
```
The Auth contract is an abstract contract that provides a flexible and updatable authorization pattern that can be used in other contracts. It has an owner and an authority (which is an instance of a contract that implements the Authority interface). It also has two events: OwnerUpdated and AuthorityUpdated.
The Auth contract has a constructor that takes an _owner and an _authority as arguments and sets the owner and authority variables to these values. It also emits the OwnerUpdated and AuthorityUpdated events.
The Auth contract has a requiresAuth modifier that checks whether the caller is authorized to call the protected function by calling the isAuthorized function and passing it the caller's address and the function signature of the protected function. If the caller is not authorized, the function will revert with the message "UNAUTHORIZED".
The isAuthorized function is an internal function that takes an address user and a bytes4 functionSig as arguments and returns a bool. It checks if the user is authorized to call the function by first checking if the authority contract is not the zero address (i.e., it has been set) and if the authority contract's canCall function returns true when passed the user, the Auth contract's address, and the functionSig. If either of these conditions is not met, it checks if the user is the owner of the contract. If either of these conditions is met, the function returns true, otherwise it returns false.
The setAuthority function is a public function that takes an instance of a contract that implements the Authority interface as an argument and sets the authority variable to this value. It also emits the AuthorityUpdated event. It has a require statement that checks if the caller is the owner or if the current authority contract's canCall function returns true when passed the caller's address, the Auth contract's address, and the caller's function signature. This is to ensure that the authority can be changed only by the owner