Code Monkey home page Code Monkey logo

hats-protocol's Introduction

Contributors Stargazers Issues Twitter


Hats Hat

Hats Protocol

How DAOs get things done

Hats Protocol Website · Report Bug · Request Feature

Table of Contents
  1. About The Project
  2. Contributing
  3. Hats Protocol Docs
  4. License
  5. Contact

About The Project

Hats Protocol is a protocol for DAO-native roles and credentials that supports revocable delegation of authority and responsibility.

Hats are represented on-chain by non-transferable tokens that conform to the ERC1155 interface. An address with a balance of a given Hat token "wears" that hat, granting them the responsibilities and authorities that have been assigned to the Hat by the DAO.

Deployments

For information on Hats Protocol versions and deployments, see Releases.

Security Audits

This project has received two security audits, listed below. See the audits directory for the detailed reports.

Auditor Report Date Review Commit Hash Notes
Trust Security Feb 23, 2023 60f07df Report also includes findings from Hats Protocol audit
Sherlock May 3, 2023 fafcfd Report also includes findings from Hats Protocol audit

Contributing

See CONTRIBUTING.md for details on how to contribute.

(back to top)

Hats Protocol Docs

Table of Contents

  1. Authorities in Hats Protocol
  2. Hats Logic
  3. ERC1155 Compatibility
  4. Wearing a Hat
  5. Hat Admins
  6. Addressable Hat Ids
  7. Eligibility
  8. Toggle
  9. Hat Mutability
  10. Hat Image URIs
  11. Creating a Hat
  12. Minting a Hat
  13. Transferring a Hat
  14. Hat Tree Grafting
  15. Renouncing a Hat

Authorities in Hats Protocol

One way to think about a Hat is as a primitive that creates a substrate onto which a DAO can attach authorities (e.g., access rights) and responsibilities via other tools (e.g., token-gating platforms).

Hats Protocol itself does not define mechanisms for how such authorities and responsibilities are associated with a Hat. All such associations are created external to the protocol.

Here are a few examples of how a DAO might confer authorities and responsibilities to a Hat:

Authority How is it attached to the Hat?
Signer on a multisig Using the Hat's ERC1155-similar token as a condition for membership in a Metropolis Pod
Admin of the DAO's Github repo Using the Hat's ERC1155-similar token as a condition for access via Lit Protocol
Leadership of a working group A social expectation

In each case, the DAO uses a separate tool to attach the authority to the Hat.

Hats is designed to be highly composable -- it will work with any tool, application, or protocol that can interact with the ERC1155 interface. Further, it allows any number of such authorities or responsibilities to be attached to a single Hat, which greatly simplifies the process for DAOs of revoking those authorities as well as the process of role handoff.

Exception: Hat Admins

Hat admins are the one (very important!) exception to the rule that authorities are external to the Hats Protocol. Refer to the Admins section below for more details.

(back to contents)

ERC1155 Compatibility

Hats Protocol conforms fully to the ERC1155 interface. All external functions required by the ERC1155 standard are exposed by Hats Protocol. This is how Hats can work out of the box with existing token-gating applications.

However, Hats Protocol is not fully compliant with the ERC1155 standard. Since Hats are not transferable by their owners (aka "wearers"), there is little need for safe transfers and the ERC1155TokenReceiver logic. Developers building on top of Hats Protocol should note that mints and transfers of Hats will not, for example, include calls to onERC1155Received.

To avoid confusion, Hats Protocol does not claim to be ERC1155-compliant. Instead, we say that Hats Protocol has "ERC1155-similar" tokens. When referring specifically to the ERC1155 interface, however, we do say that Hats Protocol conforms fully.

Hats Logic

Each Hat has several properties:

  • id - the integer identifier for the Hat, which also serves as the ERC1155-similar token id (see three paragraphs below)
  • details - metadata about the Hat; such as a name, description, and other properties like roles and responsibilities associated with the Hat. Should not exceed 7,000 characters.
  • maxSupply - the maximum number of addresses that can wear the Hat at once
  • admin - the Hat that controls who can wear the Hat
  • eligibility - the address that controls eligibility criteria and whether a given wearer of the Hat is in good standing
  • toggle - the address that controls whether the Hat is active
  • mutable - whether the hat's properties can be changed by the admin
  • imageURI - the URI for the image used in the Hat's ERC1155-similar token. Should not exceed 7,000 characters.

For more information on each property, refer to the detailed sections below.

(back to contents)

Wearing a Hat

The wearer of a given Hat is assigned the authorities and responsibilities associated with the Hat.

A wearer's status relating to a given Hat is determined by three factors. All three must be true.

  1. Whether their address has a balance (of 1) of the Hat's token
  2. Whether the Hat is active (see the Toggle section for more detail)
  3. Whether they are eligible (see the Eligibility section for more detail)

All of these factors are reflected in the Hats.balanceOf function, which will return 0 if any of the factors are false.

Any address can wear a Hat, including:

  • Externally Owned Accounts (EOAs)
  • Logic contracts (i.e., contracts with explicit logic codified within functions), or
  • Governance contracts (e.g., DAOs, multisigs, etc.)

(back to contents)

Hat Admins

The admin of every Hat is another Hat. This means that the authority to perform admin functions for a given Hat is assigned to the wearer of its admin Hat.

The scope of authority for a Hat's admin is to determine who can wear it. This is reflected in the ability to create the Hat and to mint or (for mutable Hats) transfer the Hat's token.

Hatter Contracts

Logic contracts that serve as admins are informally known as "hatter" contracts. These are contracts that implement specific logic or rules. The admin of a hatter contract is the true admin, but has delegated said admin authority to the logic embedded in the hatter.

Hatter contract logic is a wide design space for DAOs. Here are some examples of hatter logic:

  • Wearer eligibility - Enforce certain requirements that prospective wearers must meet in order to wear a given Hat, such as membership in a DAO or holding some token(s).
  • Wearer staking - One particularly important type of eligibility requirement is staking tokens, DAO shares, or some other asset as a bond that could be slashed if the wearer is not a good steward of the accountabilities associated with the Hat, or does not follow through on its associated responsibilities.
  • Hat creation - Allow certain addresses -- such as members of a DAO -- to create Hats that are then admin'd by the DAO.
  • Hat minting - Allow certain addresses -- such as members of a DAO -- to mint Hat tokens. Together with the above, a DAO could in this way enable its members to create and wear a certain type of Hat permissionlessly. This would be especially if using Hats to facilitate role clarity and legibility.

Hat Trees

The ability of a Hat to be an admin of other Hats creates the possibility for a "tree" of Hats, a structure of Hats serving as admins of other Hats. This is useful because it enables a DAO to snip off, but not destroy, a rogue branch by revoking the offending Hat. It could then re-assign that admin Hat to another wearer.

Within a given branch of a hat tree, Hats closer to the root of the tree have admin authorities for Hats further down the branch. This is consistent with the direction of delegation of authority for DAOs, and combats the tendency for accountability to dilute as delegated authorities reach the edges of a network.

Tophats

Tophats are the one exception to the rule that a Hat's admin must be another hat. A Tophat is a Hat that serves as its own admin.

The root of a Hat tree is always a Tophat. Typically, a DAO will wear the Tophat that serves as admin for the tree of Hats related to the DAO's operations.

(back to contents)

Addressable Hat Ids

Hat ids are uint256 bitmaps that create an "address" — more like an web or IP address than an Ethereum address — that includes information about the entire branch of admins for a given hat.

The 32 bytes of a hat's id are structured as follows:

  • The first 4 bytes are reserved for the top hat id. Since top hat ids are unique across a given deployment of Hats Protocol, we can also think of them as the top level "domain" for a hat tree.
  • Each of the next chunks of 16 bits refers to a single "Hat Level".

This means there are 15 total hat levels, beginning with the top hat at level 0 and going up to level 14. A hat at level 6 will have 6 admins in its branch of the tree, and therefore its id will have non-zero values at levels 0-5 as well as its own level. Since these values correspond to its admins, all that is needed to know which hats have admin authorities over a given hat is to know that given hat's id.

Hat Tree Space

A hat tree can have up to 14 levels, plus the top hat (tree root). Within those 14 levels are 224 bits of address space (remember, one level contains 16 bits of space), so the maximum number of hats in a single hat tree is $2^{224} + 1 \approx ~2.696 * 10^{67}$, or well beyond the number of stars in the universe.

Displaying Hat Ids

It is recommended for front ends to instead convert hat ids to hexadecimal, revealing the values of the bytes — and therefore the hat levels — directly.

For example, instead of a hat id looking like this under base 10: 26960769438260605603848134863118277618512635038780455604427388092416

...under hexadecimal it would look like this: 0x0000000100020003000000000000000000000000000000000000000000000000

In this second version, you can see that this hat is...

  • a level 2 hat
  • is in the first hat tree (top hat id = 1)
  • is the third hat created at level 2 within this tree
  • admin'd by the second hat created at level 1 within this tree

We can also prettify this even further by separating hat levels with periods, a la IP addresses:

0x00000001.0002.0003.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000.0000

(back to contents)

Eligibility

Eligibility modules have authority to rule on the a) eligibility and b) good standing of wearer(s) of a given Hat.

Wearer Eligibility (A) determines whether a given address is eligible to wear the Hat. This applies both before and while the address wears the Hat. Consider the following scenarios for a given address:

Eligible Not Eligible
Doesn't wear the Hat Hat can be minted to the address Hat cannot be minted to the address
Currently wears the Hat Keeps wearing the Hat The Hat is revoked

When a Hat is revoked, its token is burned.

Wearer Standing (B) determines whether a given address is in good or bad standing. Standing is stored on-chain in Hats.sol to facilitate accountability.

For example, a hatter contract implementing staking logic could slash a wearer's stake if they are placed in bad standing by the eligibility module.

An address placed in bad standing by a Hat's eligibility module automatically loses eligibility for that Hat. Note, though, that ineligibility does necessarily imply bad standing; it is possible for an address may be ineligible but in good standing.

Any address can serve as an eligibility module for a given Hat. Hats Protocol supports two categories of eligibility modules:

  1. Mechanistic eligibility are logic contracts that implement the IHatsEligibility interface, which enables the Hats contract to pull wearer standing by calling checkWearerStanding from within the Hats.balanceOf function. Mechanistic eligibility enables instantaneous revocation based on pre-defined triggers.
  2. Humanistic eligibility are either EOAs or governance contracts. To revoke a Hat, humanistic eligibility must push updates to the Hats contract by calling Hats.ruleOHatWearerStanding.

Unlike admins, eligibility modules are explicitly set as addresses, not Hats. This is to avoid long, potentially illegible, chains of revocation authority that can affect wearer penalties (such as slashed stake).

(back to contents)

Toggle

Toggle contracts have authority to switch the hat.active status of a Hat, such as from active to inactive. When a Hat is inactive, it does not have any wearers (i.e., the balance of its previous wearers' is changed to 0).

Any address can serve as a Hat's toggle. As with eligibility modules, Hats Protocol supports two categories of toggle modules:

  1. Mechanistic toggles are logic contracts that implement the IHatsToggle interface, which enables the Hats contract to pull a Hat's active status by calling checkToggle from within the Hats.balanceOf function. Mechanistic toggle enable instantaneous deactivation (or reactivation) based on pre-defined logic, such as timestamps ("this Hat expires at the end of the year").
  2. Humanistic toggles are either EOAs or governance contracts. To deactivate (or reactivate) a Hat, humanistic toggles must push updates to the Hats contract by calling Hats.toggleHatStatus.

Unlike admins, toggle modules are explicitly set as addresses, not Hats.

(back to contents)

Hat Mutability

In some cases, a Hat's properties should be immutable to give everybody (particularly the wearer(s)) maximal confidence in what they are signing up for. But this certainty comes at the expense of flexibility, which is often valuable for DAOs as they evolve and learn more about what their various roles are all about. With this trade-off in mind, Hats can be created as either mutable or immutable.

An immutable Hat cannot be changed at all once it has been created. A mutable Hat can be changed after it has been created. Only its admin(s) can make the change.

Changes are allowed to the following Hat properties:

  • details
  • maxSupply - as long as the new maxSupply is not less than the current supply
  • eligibility
  • toggle
  • mutable - this is a one-way change
  • imageURI

Additionally, mutable hats can be transferred by their admins to a different wearer. Immutable hats cannot be transferred.

TopHat Exception

The only exception to the above mutability rules is for tophats, which despite being immutable are allowed to change their own details and imageURI (but not other properties).

Note that this only includes non-linked tophats; a tophat that has been linked (aka grafted) onto another hat tree is no longer considered a tophat, and therefore is subject to the same mutability rules as other hats.

(back to contents)

Hat Image URIs

Like any other NFT, Hats have images. The image for a given Hat is determined by the following logic:

  1. If the Hat's imageURI property is set, use that
  2. If the Hat's imageURI property is not set, then use the imageURI of the Hat's admin Hat
  3. If the admin Hat's imageURI property is not set, then use the imageURI of that Hat's admin
  4. Repeat (3) until you find an imageURI that is set
  5. If no set imageURI is found within the original Hat's hat tree (including the Tophat), then use the globalImageURI set in the Hats Protocol contract

This logic creates flexibility for DAOs to efficiently customize images for their Hats, while keeping images as optional.

(back to contents)

Creating a Hat

The creator of a Hat must be its admin. In other words, the admin of a Hat must be the msg.sender of the Hats.createHat function call. Though remember, by delegating its authority to a hatter contract, an admin can enable eligible others to create Hats based on whatever logic it desires.

Creating a Tophat (a Hat that serves as its own admin) requires a special function mintTophat, which creates a new Hat, sets that Hat as its own admin, and then mints its token to a _target. Any address wanting to create a Hat that is not already wearing an admin Hat of some kind must first create a Tophat with itself as the wearer.

Batch Creation

In some scenarios, a DAO may want to create many Hats at once -- including an entire hat tree -- at once. This is particularly useful when setting up an initial structure for a DAO or working group (e.g., from a Hats template) or when forking an existing Hats structure from a template.

Enabling this latter forking/exit scenario is an important protection for Hat wearers against potential abuse of power by their DAO.

To create a batch of Hats, a DAO can call the Hats.batchCreateHats() function. This function takes arrays as its arguments, from which it constructs multiple Hats. As long as each of these Hats is part of the same tree of Hats — i.e., they either have the same existing Hat or any of the newly created Hats as admin(s) — they can all be created together.

(back to contents)

Minting a Hat

Only a Hat's admin can mint its token to a wearer.

To mint a Hat, the Hat must be active, its max supply must not have already been reached, the target wearer must not already wear the Hat, and the target wearer must be eligible for the Hat.

A Hat's admin can mint its token individually by calling Hats.mintHat.

Batch Minting

An admin can also mint multiple Hats by calling Hats.batchMintHats. This enables an admin to mint instances of the same hat to multiple wearers, to mint several Hats at once, or even to mint an entire Hats tree it just created.

(back to contents)

Transferring a Hat

Only a Hat's admin can transfer its token(s) to new wearer(s).

Unlike typical tokens, the wearer of a Hat cannot transfer the Hat to another wallet. This is because the authorities and responsibilities associated with a Hat are delegated to, not owned by, the wearer.

As a result, there is no need for safe transfers (transfers which check whether the recipient supports ERC1155) or to pass data to recipient on1155Received or onERC1155BatchReceived hooks.

For these reasons, in Hats Protocol, the standard ERC1155 transfer functions — safeTransferFrom and safeBatchTransferFrom are disabled and will always revert. Similarly, token approvals are not required and setApprovalForAll will always revert.

As a replacement, Hats can be transferred by admins via Hats.transferHat, which emits the ERC1155 standard event TransferSingle. Transfer recipients must not already be wearing the hat, and must be eligible to wear the hat.

With the exception of tophats — which can always transfer themselves — only mutable Hats can be transferred. Inactive Hats cannot be transferred.

Hat Tree Grafting

Not all Hats trees will unfurl from top down or inside out. Sometimes, new branches will form independently from the main tree, or multiple trees will form before a main tree even exists.

In these cases, Hat trees can be grafted onto other trees. This is done via a request-approve process where the wearer of one tree's topHat requests to link their topHat to a hat in another tree, whose admin can approve if desired. This has three main effects:

  1. The linked tophat loses its topHat status (i.e., Hats.isTopHat will return false) and turns into what we call a "tree root" or "linked topHat", and
  2. The hat to which it is linked becomes its new admin; it is no longer its own admin
  3. On linking, the linked topHat can be assigned eligibility and/or toggle modules like any other hat

Linked Hat trees can also be unlinked by the tree root from its linked admin, via Hats.unlinkTopHatFromTree. This causes the tree root to regain its status as a top hat and to once again become its own admin. Any eligibility or toggle modules added on linking are cleared. Note that unlinking is only allowed if the tree root is active and has an eligible wearer.

⚠️ CAUTION: Be careful when nesting multiple Hat trees. If the nested linkages become too long, the higher level admins may lose control of the lowest level Hats because admin actions at that distance may cost-prohibitive or even exceed the gas limit. Best practice is to not attach external authorities (e.g. via token gating) to Hats in trees that are more than ~10 nested trees deep (varies by network).

Relinking

Linked topHats can be relinked to a different Hat within the same tree. This is useful for DAOs that want to reorganize their subtrees without having to go through the request and approve steps. Valid relinks must meet the following criteria in order to ensure security:

  1. The Hat wearer executing the relink is an admin of both the linked topHat and the new admin (destination)
  2. The new admin (destination) is within the same local tree as the existing admin (origin), or within the tippy top hat's local tree. Tippy top hats executing a relink are not subject to these restrictions.
  3. The new link does not create a circular linkage.

Renouncing a Hat

The wearer of a Hat can "take off" their Hat via Hats.renounceHat. This burns the token and revokes any associated authorities and responsibilities from the now-former wearer, but does not put the wearer in bad standing.

(back to contents)

Multicall

This contract inherits from Multicallable, which adds a non-payable multicall function to the contract. This enables EOAs to make multiple calls to the contract atomically, unlocking a number of useful batch operations that were previously only available to smart contracts.

License

Distributed under the AGPLv3 License. See LICENSE.txt for more information.

(back to top)

Contact

Spencer Graham - @spengrah

nintynick - @nintynick_

David Ehrlichman - @davehrlichman

Project Website: https://hatsprotocol.xyz/

Project Link: https://github.com/Hats-Protocol/hats-protocol/

(back to top)

hats-protocol's People

Contributors

davehrlichman avatar nintynick avatar omahs avatar spengrah avatar topocount avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

hats-protocol's Issues

Hats details could support structured metadata

How might hats.details support more detailed and/or structured metadata about the hat? Ideally it would also be surfaceable

initial ideas

  • uri that points to some content -- eg arweave, IPFS, or even web2 api
  • the content could be a json object with properties of its own -- eg overview, authorities, responsibilities, etc
  • we could even devise a standard for that json object

Human-readable hat ids

Hat ids are uint256 bitmaps that use the following structure to achieve an "address" for each hat that includes information about the tree of admins for the hat:

  • The first 4 bytes are reserved for top hat id (we can think of this as the "domain" of a given hat, often corresponding to the DAO the hat belongs to)
  • The remaining 28 bytes each represent a single hat level, fitting 1 admin hat and 255 "child" hats.

Displaying the id as a binary number would require way too much space, but displaying it as a base-10 number occludes the above address semantics. Displaying the id in hex is a good compromise, since it exposes bytes as described above.

But, starting with hex we can make hat ids even more human-readable by...

  1. inserting dots between hat levels
  2. trimming trailing hat levels (trailing empty bytes)

The result would look something like this:
For hat id 0x0000000101010000000000000000000000000000000000000000000000 -- which is the first hat at the second hat level, whose admin is the first hat at the first hat level, whose admin is the first top hat -- the human-readable id would be...

00000001.01.01 or perhaps even 1.01.01

Add support for multimedia uris on each hat

This would allow DAOs (or other users of Hats) to assign custom images (or videos, or audio) to their hat tokens

Spec

  • Add a new image member to the hat struct model
  • Add a global image uri field
  • Add logic to _constructUri that defaults to the hat's admin's image uri unless the hat's image uri is non-empty. A topHat's image defaults to the global image uri. This way, the logic works for the entire hat tree

Build a HatOwnable contract for modular hat-based on-chain access control

Instead of the owner being set to an address, its instead set to the wearer(s) of a given hat

This would be a library or abstract contract other projects could use to make their contracts/protocols work with Hats.

Possible extensions:

  • full fork of oz access control contracts that replaces addresses with hat wearers
  • full fork of solmate auth contracts that replaces addresses with hat wearers

additional test coverage for HatsIdUtilities library

#58 refactored functions related working with hat ids into a library to be inherited into Hats.sol. This makes the contract more readable, and it also has the benefit of making those functions more readily available by other developers.

With that in mind, its important to ensure that the library works across a wider array of test cases than are narrowly required for Hats.sol alone.

graph instead of a tree

How might the protocol enable hats to take a more generalized graph structure rather than specifically a tree structure?

Currently, hats can only be created in the form of a tree structure. This is efficient, and it covers many organizational structures (and yet more can work with it via hatter contracts), yet it imposes some constraints where perhaps there may be circumstances or use cases where fewer constraints may be valuable.

thought starters:

  • create a HatAdmin struct that can take several adjustable properties to define the type of edge between the admin the hat
  • move to a more explicit edge and node approach

On-chain hat lineage?

What is the history of hat wearers? Should that be on chain? Or does it just need to be legible off chain?

Hat mutability research

Currently, hats are immutable in the sense that their properties -- ie max supply, details, imageURI, toggle, and eligibility -- cannot by changed once the hat is created.

This has the benefit of limiting the governance / attack surface for hats, but at the cost of flexibility for DAOs. For example, a DAO may want to add mechanistic eligibility logic to an existing hat after experimenting manually with several candidate approaches. Currently, this would require creating and minting a brand new hat.

This is a research issue, with the goal of exploring the following questions:

  1. What is the design space for hats mutability? On what dimensions could mutability be introduced partially or with constraints?
  2. What are the risks and trade-offs of the dimensions from (1)?
  3. What is a recommended approach for hats mutability?

Create usability tests

The intention of this issue is to create a set of holistic manual tests, in excess of the functional tests outlined in #24.

Three examples Hat trees to build out, to start:

Image

Enable a lower-friction way to create and mint a hat

The existing create hat -> submit offer -> accept offer flow may have too much friction for role legibility use cases. We should explore how hats might optionally be created (potentially by the wearer?) without going through that full flow, even when not owned by the wearer

ensure that hat wearers can fork hats and a hat tree

the DAO (the wearer of the topHat) owns the authorities & powers it delegates to various hats, so those authorities and powers should not be forkable. However, the structure of the delegation and "hat tree" should be forkable and able to be (easily?) duplicated under the control of a different topHat.

Potential for out-of-gas errors

Summary

The batchCreateHats function loops through arrays of potentially large sizes. If these arrays become too huge, the transaction may fail due to a lack of gas.

Vulnerability Detail

function batchCreateHats(

The function batchCreateHats loops through several arrays of potentially large sizes to create hats. This means that for each hat creation, a certain amount of gas is required. If the arrays are too large, there is a risk of running out of gas and the transaction failing. This could happen because the amount of gas specified by the user may not be enough to cover the gas costs of creating all the hats.

Impact

The function will consume more gas than expected, resulting in an out-of-gas error and transaction failure.

Code Snippet

 for (uint256 i = 0; i < length;) {
            createHat(
                _admins[i],
                _details[i],
                _maxSupplies[i],
                _eligibilityModules[i],
                _toggleModules[i],
                _mutables[i],
                _imageURIs[i]
            );

            unchecked {
                ++i;
            }
        }

        success = true;

Tool used

Manual Review

Recommendation

Properly estimate the gas costs of creating the hats and to ensure that the gas limit specified by the user is sufficient. We can also split the creation of the hats into smaller batches to reduce the amount of gas used for each batch.

Add a wallet to a hat

Potentially deploy a proxy contract attached to each hat. This would...

  1. give each hat a wallet to receive and send funds
  2. potentially allow a hat interact with other smart contract protocols directly rather than requiring that those protocols support erc1155

Bring back solmate submodule

In e23abeb, we removed the solmate submodule in favor of storing select contracts in lib. This was done primarily to avoid errors with github actions observed in immediately previous pushes and PRs.

This issue would bring back the solmate submodule (assuming the problems with github actions can be addressed).

Consider embedding the topHat id into the id of its child hats

The current pure integer approach to hat ids is dead simple, but the result is that identifying which topHat a given Hat is associated with requires traversing the full hat tree back to its root (the topHat).

An id scheme that combined the topHat id with the hat's id would enable much easier and more flexible identification of a hat's domain (ie topHat).

Even further, perhaps a Hat id could include information about the entirety of its own branch of its hat tree??

reimplement `createHatsTree`

With addressable ids (#19), hats trees can likely be modeled in a more sophisticated way than previously. This also means that createHatsTree will need to be implemented;

Reference: old approach to createHatsTree:

hats-protocol/src/Hats.sol

Lines 168 to 223 in 8f40b1e

/// @notice Creates a tree new Hats, where the root Hat is under admin control by the msg.sender. Especially useful for forking an existing Hat tree or initiating a template Hat tree structure.
/// @dev The admin for each Hat must exist before the Hat is created, so Hats must be created before Hats for which they serve as admin
/// @param _details Descriptions of the Hats
/// @param _maxSupplies The total instances of the Hats that can be worn at once
/// @param _firstAdmin The hatId of the admin of the first Hat to create; it must already exist
/// @param _adminOffsets The deltas between the ids of the Hats that will control who wears the newly created hat
/// @param _oracles The addresses that can report on the Hat wearers' standings
/// @param _conditions The addresses that can deactivate the Hats
function createHatsTree(
string[] memory _details,
uint32[] memory _maxSupplies,
uint64 _firstAdmin,
uint64[] memory _adminOffsets, // _adminOffsets.length + 1 = _details.length
address[] memory _oracles,
address[] memory _conditions
) public {
// check that array lengths match
uint256 length = _maxSupplies.length; // saves an MLOAD
bool lengthsCheck = ((_details.length == length) &&
(length == _adminOffsets.length + 1) &&
(length == _oracles.length) &&
(length == _conditions.length));
if (!lengthsCheck) {
revert BatchArrayLengthMismatch();
}
// create a new Hat for each qualifying item
for (uint256 i = 0; i < length; ++i) {
// calculate the admin id for this Hat
uint64 admin;
if (i == 0) {
// first Hat created gets the _firstAdmin
admin = _firstAdmin;
} else {
/* subsequent Hats are assigned admins based on an offset.
Example: if nextHatId is 10, and the admin of the next Hat we want to create is id 8, then the offset would be 2.
*/
admin = nextHatId - _adminOffsets[i - 1];
}
/* Only create the new Hat if it would not be a topHat (a Hat that is its own admin) and if the msg.sender serves as its admin, otherwise skip to the next item.
This requires that Hats must be created prior to any Hat(s) they are an admin for.
*/
if ((admin != nextHatId) && isWearerOfHat(msg.sender, admin)) {
_createHat(
_details[i],
_maxSupplies[i],
admin,
_oracles[i],
_conditions[i]
);
}
}
}

Bug: vector for arbitrarily increasing a hat's supply

@zobront identified the following bug:

hey, just took a quick skim without context but think i found one issue.

are hat admins able to update toggle directly on toggle contract, without permission from the Hats.sol contract?

if that's the case, then we can arbitrarily ramp up supply variable (regardless of actual balances minted) because:

  • _mintHat() sets balanceOf = 1 instead of incrementing, while it always increments supply by 1
  • the external mintHat() function checks isWearerOfHat() before allowing internal to be called
  • but isWearerOfHat() checks balanceOf(), which overrides and returns zero if toggle is turned off

so i think an admin could toggle off, mint an arbitrary number of hats, which all just increment the totalSupply, and then turn toggle back on to reactivate the hat.

could this be used to ramp up supply to max and lock anyone else out from minting? i'm not sure how permissions work in the > system and whether this could actually be abused by a lower level admin in any way.

Sample IHatsConditions.sol contract

Write a sample contract that conforms to the IHatConditions interface.

A couple ideas:

  • Timestamp expiry
  • Hook for a governance contract to toggle

Hats wearing other hats

Hats represent roles, conferring responsibilities, accountabilities and (via token-gating) their associated authorities/permissions on the wearer. Currently, all of those affordances are bundled into a single hat. This is really great, since it does away with the artificial separation of responsibility and permissions so common in organizations today. When a responsibility requires permissions, the two should go together.

But, this bundling isn't always ideal for all types of roles. Sometimes roles can change over time, requiring new permissions or no longer needing old permissions. And some roles can even change based on who is filling them. For example, one lead developer may have have a design background and need write access in figma, while her successor may be the world's worst designer and should not have figma write access.

So ideally, Hats Protocol should be flexible enough to enable the permissions and authorities of hats to be remixed and adjusted. One particularly elegant way to accomplish this would be to enable hats to wear other hats. If implemented, this would have the following advantages:

  1. New permission N could be attached to an existing Hat A simply by "minting" Hat P to Hat A
  2. Outdated permission O could be removed from an existing Hat A simply by revoking Hat O from Hat A
  3. Since Hats could wear many Hats, individual Hats could have atomic, one-to-one relationships with access permissions / authorities

HOWEVER, implementing this is a hard problem. A solution may require a significant re-architecture of the protocol or an extremely innovative solution.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.