Hats Protocolを理解する!!
はじめに
みなさん、 Hats Protocol というプロトコルを聞いたことがありますか??
Hats Protocolについて調べる機会があったのでその結果をブログ記事にまとめました!!
Hats Protocolとは何か?
まずは、 Hats Protocol がどういうプロトコルなのか解説します!
Hats Protocolとは、組織の役割(ロール)や権限をプログラム可能なデジタルグラフに変換し、オンチェーンで管理できるようにするためのプロトコルです!!
特徴は次の点ですね!
AWSのIAMロールの考え方に近いかもしれませんね。
サポートしているチェーン一覧は下記から確認できます。
テストネットでは Sepolia と Holeskyが対応してそうです。
- Sepoliaにデプロイされたコントラクト
- Holeskyにデプロイされたコントラクト
クイックスタート
まずは、下記手順に従ってサンプル組織ツリーを作ってみます!!
1. 新しいツリーを作成する
まず、以下のページにアクセスします!!
ロゴやTop of Hatsの名前を入力する必要があるみたいです!!
諸々必要なデータ入力します!
今回はこんなロゴを用意しました!!
全部入力したら Createボタンを押します!!
これで HatがNFTとしてミントされます!!
実際のトランザクションは以下から確認できます!!
しばらく待つと以下のような画面に遷移します!!
これでツリーができました!!
2. 最初の子Hatを作成する
続いて子Hatを作成します!!
先ほどの画面の左上の Edit Tree をクリックします!!
子Hatも細かく設定できるみたいですね!!
名前や画像なども決められるみたいです!
その他、帽子を被れるアカウント数の上限値設定、アドレスの一括登録などが可能なんですね。
他にも細かく権限周りを設定することができるみたいです!!
これで子Hatが作成できました!!
クイックスタートはここまでです!!
Hats Protocolの機能を細かく確認していこう!!
Adminにできること
Hats Protocolでは Admin は特別な権限を持ちます。
子Hatの詳細や画像、最大供給量、適格アドレス、トグルアドレスを変更できます(ただし、変更可能な状態のHatに限る)。管理者Hatは、新しい着用者に対して、変更可能なHatや変更不可能なHatを発行でき、変更可能なHatを別のアドレスに移転することも可能みたいです。
各Hatは、その下に直接リンクされたすべてのHatの管理者として機能します。
そして Hatは、EOAのみならずマルチシグなどのコントラクトウォレットやスマートアカウントウォレットにも着用が可能とのことです!
これは面白いですね!!!
これなら柔軟に権限の管理ができそうです!!
他のプロトコルとの連携
以下のページで紹介されている通り、様々なプロトコル・アプリと連携が可能とのことです!!!
SafeやSuperfuildなどのブロックチェーン側のプロトコルだけでなく、テレグラムやDiscord、Google Docsとも連携できるみたいですね。
HatsProtocolの機能を使って以下のようなユースケースをさらに強力にできるみたいですね!
-
アカウントとリソース:
Ethereumアカウントの管理、マルチシグの署名権限、報酬管理 -
コミュニケーションチャンネル:
共有コミュニケーションチャンネルやアカウントへのアクセス権や投稿権限 -
ガバナンスと投票:
スマートコントラクトへのオンチェーン権限、提案作成、投票権 -
ワークスペース:
ワークスペース、ファイル、ドキュメントでの読み取り、レビュー、書き込み権限
トークンゲート機能との連携
Hatで管理できる権限には2種類の権限があります。
-
ハードパワー:
トークンゲートを通じて提供されるオンチェーンやオフチェーンの明確な権限 -
ソフトパワー:
会議の進行や特定のドメインでのプロジェクト作業、トークンゲートで管理できないアプリの管理権限
これらの権限をHatとしてブロックチェーン上で作った後にトークンゲートを通じてHatに結びつけることもできるみたいです。
-
トークンゲーティングプラットフォーム:
Guild、Collab.Land、Lit Protocolなどがトークンゲーティングを提供し、以下のアプリで使用できます:- Guild:
Coordinape、
Discord、
Github(プライベートリポジトリのみ)、
Google Workspace(Docs、Sheets、Slides)、
Party.Space、
POAP、
Rally、
Telegram、
Wonderverse - Collab.Land:
Discord、
Telegram - Lit Protocol:
Gather Town、
Google Drive、
Headline、
IPFS、
Nowhere、
Orbis
Club、
Shopify、
WalletChat、
Wordpress、
Zoom
- Guild:
-
トークンゲーティングを統合したアプリ:
Charmverse、Wonderverse、Commonwealth(近日対応予定)など、特定のチャンネルへのアクセスや読み書き権限を提供 -
ERC1155を読み込んでパラメータを変更するツール:
Snapshot Strategiesを使用し、特定のHatのみがSnapshot提案で投票できるようにしたり、特定のHatに投票権の重みを与えたりする -
HatsSignerGate:
Safe multisig署名権限を特定のHatに提供するZodiacモジュール -
ソーシャルアグリーメントや他のアカウンタビリティメカニズム:
季節ごとの選挙、ステーキング要件、評判システムなど。トークンゲーティングを通じて明確に権限を付与できない場合でも、そのHatがその権限を持つことを示すためにHatを使うことができます。以下のサイトからそれぞれのアプリとどのように統合すれば良いのかまとめられています!!
承認と適格性: Hatの保持者に必要な条件について
Hatを通じてアクセスできる権限は、個人またはグループの管理者によって付与および取り消しが可能です。また、Hatsモジュールを使用することで、幅広い自動化された適格性やアカウンタビリティの基準に基づいて管理することもできるみたいです。
-
Hatsモジュール
Hatsモジュール は、役割のためのプログラム可能な拡張機能です。
モジュールをHatに接続することで、特定の条件に基づいてHat(および関連する権限)の自動付与および取り消しが可能になります。
-
Hatの適格性モジュール
Hatの適格性モジュールは、どのアドレスがそのHatを着用できるかを決定するアドレスです。
また、着用者がもはや適格でない場合には、そのHatを取り消すことができます。
適格性モジュールは、手動で適用することもコントラクトウォレットによる自動トリガーで実行することできるみたいです。
-
適格性の基準について
権限の付与の基準については結構いろんなルールを決められるみたいですね。
-
トークン、NFT、アテステーション:
ERC20トークンの残高、ERC721 NFTs、ERC1155 NFTのトークンID、EASアテステーションなど -
選挙と許可リスト:
JokeRace、Snapshot、Tallyからの選挙結果、自動任期制限、手動で作成された許可リスト -
成果と評判:
バッジ、オンチェーンポイント、Colinks、Gitcoinパスポートスコアなど -
前提条件のアクション:
ステーキング、契約の署名、他の役割の保持、サブスクライバーであることなど -
その他:
複数の基準をAND/ORロジックで組み合わせたり、AIエージェントを導入したり、細かなオンチェーンまたはオフチェーンのトリガーを作成したりすることができます。
ルールについては開発者が柔軟に決められるみたいですね。
また、Hatsモジュールを利用することもありみたいですね。もちろん、Appからも設定ができます。
クイックスタートの部分で子Hatを作った時に Revocation & Eligibilityというセクションが出てきたと思いますがそこで設定できるみたいです!!
利用可能な適格性モジュールはいくつか種類があるみたいで以下のページでリスト化されています!!
-
トークン、NFT、アテステーション:
技術的に Hats Protocolを細かく確認していこう!!
冒頭にも説明しましたが、Hatsは 譲渡不可能なERC1155ライクなトークンとして発行されます!!
Hats Protocolの主要なロジックは、Hatsの作成、発行、取消、管理となっています。
特定のHatの動作を拡張し、カスタマイズすることも可能みたいです!!
具体的にどんな仕様・機能が提供されているか確認していきます!
Hat Properties
Hatsが持つプロパティを確認していきます!!
ERC1155がベースになっているのでERC1155のNFTに似てますね。
Wearing a Hat
Hatを着用できるかの条件は3つ
Hats Protocolには、isWearerOfHat()
という便利な関数があり、これはbalanceOf()
をラップして、残高が1であるかどうかをブール値で返すみたいです。
Hat Admins & Hatter Contracts
各帽子の管理者は別の帽子です。つまり、特定の帽子の管理機能を実行する権限は、その管理帽子を着用している人に割り当てられます。
管理帽子を着用しているアカウントは誰が帽子を着用できるか決定することが可能です。
そして管理権限は伝播します。ある帽子のすべての祖先(直接の管理者、その管理者の管理者、など)は、その帽子の管理者として機能できます。
-
帽子に対するコントロール
役割 権限 管理者 新しい帽子を作成する 着用者に帽子を発行する 帽子のプロパティを編集する(変更可能な場合) 帽子を移転する(変更可能な場合) 対象条件モジュール 対象外のアドレスが帽子を着用するのを防ぐ 特定の着用者から帽子を剥奪する トグルモジュール 帽子をアクティブまたは非アクティブにする ⇒ 全ての着用者がその帽子を失う -
Hatter Contracts
管理者として機能するロジックコントラクトは、Hatter Contractsと呼ばれます。これらは、特定のロジックやルールを実装するコントラクトです。Hatter Contractsの管理者は真の管理者ですが、その管理権限をハッターに埋め込まれたロジックに委任しています。
Hatter Contractsのロジックは、DAOにとって広範なデザイン領域です。
以下は、Hatterロジックの例です:
-
帽子の作成
特定のアドレス(DAOのメンバーなど)に、DAOが管理する帽子を作成する権限を付与します。 -
帽子の発行
特定のアドレス(DAOのメンバーなど)に、帽子トークンを発行する権限を付与します。
上記と組み合わせることで、DAOはメンバーが特定のタイプの帽子を許可なく作成し、着用できるようにすることができます。これは、役割の明確化と理解を促進するために帽子を使用する場合に特に有効です。
-
-
着用者の対象条件
特定の帽子を着用するために、候補者が満たすべき要件を強制します。例えば、DAOのメンバーであることや、特定のトークンを保持していることなどです。これは、多くの場合、対象条件モジュールとして実装する方が効果的です。
-
着用者のステーキング
特に重要な対象条件の一つに、トークンやDAOシェア、その他の資産を担保としてステーキングすることがあります。これは、着用者が帽子に関連する責任を果たさなかったり、義務を遂行しなかった場合に、担保が削減されるリスクがあることを意味します。これも、多くの場合、対象条件モジュールとして実装する方が効果的です。
Hats Trees
すべての帽子が別の帽子を管理者として持つという事実は、すべての帽子が「Hats Trees」の中に存在することを意味します。
このツリー構造が、組織の帽子の基盤を形成します。
特定の帽子ツリーの枝の中では、ツリーの根に近い帽子が、その枝の下位にある帽子に対する管理権限を持っています。
これは、DAO(分散型自律組織)の権限委任の方向性と一致しており、ネットワークの端に権限が委任されるにつれて責任が希薄化する傾向に対抗します。
-
トップハット
トップハットは、帽子の管理者が必ず別の帽子でなければならないというルールの唯一の例外です。
トップハットは自分自身を管理者とする帽子です。帽子ツリーの根は常にトップハットです。
通常、DAOは、その運営に関連する帽子のツリーを管理するトップハットを着用します。 -
帽子ツリーと帽子ID
各帽子ツリーは最大で15の深さを持ち、各帽子は最大で2^16 = 65,536の子供を持つことができ、このパターンが14回繰り返されます。各帽子のIDには、そのツリーのIDとツリー内での位置が含まれます。
Hat IDs
Hat IDは、ツリーの中で帽子がどこに位置しているか、管理者の全枝を含む情報を持つ「アドレス」を作成するためのuint256ビットマップです。
これは、Ethereumのアドレスというよりも、WebやIPアドレスに近いものです。
帽子IDの32バイトは以下のように構成されています:
最初の4バイトはトップハットIDに予約されています。
トップハットIDはHats Protocolの特定のデプロイメント全体で一意であるため、これらを帽子ツリーのトップレベル「ドメイン」と考えることができます。
次の各16ビットのチャンクは、1つの「帽子レベル」を指します。
帽子レベルは合計で15レベルあり、レベル0のトップハットから始まり、レベル14まで続きます。
次のようなHat ID(16進数)を考えてみましょう:
0x0000000f00020005000a00010000000000000000000000000000000000000000
便宜上、IPアドレスのように短縮形に再フォーマットできます:15.2.5.10.1
このIDだけで、この帽子について多くのことがわかります:
Eligibility Modules
Eligibility Modules(適格性モジュール)には、特定の帽子の着用者に関して以下の2つの権限があります。
-
a) 適格性
-
b) 良好な状態にあるかどうか
-
着用者の適格性
着用者の適格性 (A) は、特定のアドレスがその帽子を着用する資格があるかどうかを判断します。
これは、そのアドレスが帽子を着用する前および着用中の両方に適用されます。以下のシナリオを考えてみましょう。
適格 非適格 帽子を着用していない場合 帽子をアドレスに発行できる 現在帽子を着用している場合 帽子を着用し続ける -
着用者の状態
着用者の状態 (B) は、特定のアドレスが良好な状態にあるか、悪い状態にあるかを判断します。
状態は、Hats.solにオンチェーンで保存され、責任を明確にします。
例えば、ステーキングロジックを実装しているHatter Contractでは、適格性モジュールによって悪い状態にされた場合、着用者のステークを削減することができます。
特定の帽子の適格性モジュールによって悪い状態にされたアドレスは、自動的にその帽子の適格性を失います。ただし、非適格であることが必ずしも悪い状態を意味するわけではなく、あるアドレスが非適格であっても、良好な状態である可能性があります。
任意のアドレスが特定の帽子に対する適格性モジュールとして機能することができます。
Hats Protocolは、次の2つの種類の適格性モジュールをサポートしています:-
機械的適格性
IHatsEligibility
インターフェースを実装するロジックコントラクトであり、Hats.solコントラクトがHats.balanceOf
関数内からcheckWearerStandingを呼び出すことで、着用者の状態を即座に確認できます。事前定義されたトリガーに基づいて即時に帽子を取り消すことが可能です。 -
人間的適格性
EOAやガバナンスコントラクト(DAOなど)です。人間的適格性では、帽子を取り消すために、HatsコントラクトにHats.ruleOHatWearerStandingを呼び出して状態を更新する必要があります。
管理者とは異なり、適格性モジュールは帽子ではなくアドレスとして明示的に設定されます。これは、着用者に対する罰則(ステークの削減など)に影響を与える長くて読みづらい可能性のある取り消し権限の連鎖を避けるためです。
-
Toggle Modules
Toggle Contractは、帽子のhat.active
ステータスをアクティブから非アクティブに切り替える権限を持っています。帽子が非アクティブになると、その帽子の着用者は存在しなくなり(つまり、以前の着用者の残高が0に変更されます)。
任意のアドレスが帽子のトグルとして機能することができます。適格性モジュールと同様に、Hats Protocolは2つのカテゴリのToggle Modulesをサポートしています:
-
機械的トグル
IHatsToggleインターフェースを実装するロジックコントラクトであり、Hats.balanceOf関数内からcheckToggleを呼び出すことで、帽子のアクティブステータスを即座に確認し、非アクティブ化(または再アクティブ化)を行います。例えば、「この帽子は年末に期限切れになる」といった事前定義されたロジックに基づいて、即座に非アクティブ化することが可能です。 -
人間的トグル
EOAやガバナンスコントラクト(DAOなど)です。帽子を非アクティブ化(または再アクティブ化)するためには、人間的トグルがHatsコントラクトにHats.toggleHatStatusを呼び出して状態を更新する必要があります。
管理者とは異なり、適格性モジュールと同様に、トグルモジュールは帽子ではなく、アドレスとして明示的に設定されます。
Hat Mutability and Editing
場合によっては、帽子の特性が不変であることが、特に着用者にとって、何に同意しているかについて最大限の信頼を提供するために重要です。しかし、この確実性は柔軟性の欠如を意味し、柔軟性はDAOが進化し、様々な役割について学んでいく中で価値を持つことが多いです。このトレードオフを考慮して、Hatsは不変または可変のいずれかで作成することができます。
不変な帽子は、一度作成されると一切変更することができません。
可変な帽子は、作成後に変更することが可能です。変更はその帽子の管理者のみが行えます。
以下の帽子の特性に対する変更が許可されています:
- 詳細
- 最大供給量(新しい最大供給量が現在の供給量を下回らない限り)
- 適格性
- トグル
- 可変性(これは一方向の変更です)
- 画像URI
さらに、可変な帽子は管理者によって異なる着用者に移行することができます。不変な帽子は移行することができません。
-
トップハットの例外
上記の不変性ルールの唯一の例外は、トップハットです。不変でありながら、自身の詳細と画像URIを変更することが許可されています(ただし、他の特性は変更できません)。
Creating Hats
帽子を作成する者は、その帽子の管理者でなければなりません。言い換えれば、帽子の管理者はHats.createHat
関数を呼び出す際のmsg.sender
である必要があります。
ただし、管理者が自らの権限をHatter Contractに委任することで、その管理者は、任意のロジックに基づいて他の適格者に帽子を作成させることができます。
トップハット(自分自身が管理者である帽子)を作成するには、特別な関数mintTophat
が必要です。
この関数は、新しい帽子を作成し、その帽子を自分自身の管理者として設定し、その後、トークンを_target
にミントします。管理者帽子をまだ持っていないアドレスが帽子を作成したい場合、まず自分自身を着用者としてトップハットを作成する必要があります。
DAOが一度に多くの帽子を作成したい場合、特に全体の帽子ツリーを一度に作成する場合に、バッチ作成が役立ちます。これは、DAOやワーキンググループの初期構造をセットアップする際(例:帽子のテンプレートから)や、既存の帽子構造をテンプレートからフォークする際に特に有用です。
複数の帽子をバッチ作成するために、DAOはHats.batchCreateHats()
関数を呼び出すことができます。この関数は配列を引数として受け取り、そこから複数の帽子を構築します。これらの帽子が同じ帽子ツリーの一部である限り(すなわち、既存の帽子または新たに作成された帽子が管理者である場合)、一度にすべての帽子を作成することが可能です。
Minting Hats
帽子のトークンをミントできるのは、その帽子の管理者だけです。
帽子をミントするには以下の条件を満たす必要があります:
-
バッチミント
管理者はHats.batchMintHatsを呼び出して、複数の帽子を一度にミントすることも可能です。これにより、同じ帽子を複数の着用者にミントしたり、複数の帽子を一度にミントしたり、さらには新しく作成した帽子ツリー全体をミントすることもできます。
Transfering Hats
HatはERC1155ライクなトークンとして発行されるみたいですが、別のアカウントに移転できるのはその帽子の管理者だけのようです。
これは、帽子に関連する権限と責任が、着用者に委任されているものであり、所有されているものではないためです。
そのため、安全な転送(受信者がERC1155に対応しているかを確認する転送)や、受信者にデータを渡すためのon1155Received
やonERC1155BatchReceived
フックは不要です。
このため、Hats Protocolでは、標準のERC1155転送機能であるsafeTransferFrom
およびsafeBatchTransferFrom
は無効化され、常にエラーが返されます。同様に、トークンの承認は必要なく、setApprovalForAll
も常にエラーが返されます。
-
ERC1155互換性
代替として、帽子は管理者によってHats.transferHat
を通じて移転され、ERC1155標準イベントTransferSingle
が発行されます。移転の受信者は既にその帽子を着用しておらず、帽子を着用する資格がある必要があります。
トップハット(常に自分自身を移転できるもの)を除き、移転可能なのは、変更可能かつアクティブな帽子のみです。
Renouncing Hats
帽子の着用者は、Hats.renounceHat
を通じて自分の帽子を「脱ぐ」ことができます。
これにより、その帽子のトークンは焼却され、元の着用者から関連する権限と責任が取り消されますが、その着用者が不良な立場に置かれることはありません。
Batch Actions
バッチ作成やバッチミントに加えて、Hats.solの関数を一連の処理としてまとめて一つのトランザクションで実行することができます。
これは、Hats.solが継承しているMulticallable
によって可能になっています。
この機能は、支払い不要のmulticall
関数をコントラクトに追加します。これにより、EOAはコントラクトに対して複数の呼び出しを一括で行うことができ、これまでスマートコントラクトでしか利用できなかった便利なバッチ処理が可能になります。
Hat Image URIs
他のNFTと同様に、ハットには画像があります。
特定のハットの画像は、次のロジックに従って決定されます。
ERC1155 との互換性
Hats Protocolは、ERC1155インターフェースに完全に準拠しています。
ERC1155標準で求められるすべての外部関数がHats Protocolによって公開されており、これによりハットは既存のトークンゲートアプリケーションとすぐに連携できます。
ただし、Hats ProtocolはERC1155標準に完全に準拠しているわけではありません。ハットは所有者(つまり「着用者」)によって移転できないため、安全な移転やERC1155TokenReceiver
ロジックの必要性はほとんどありません。
Hats Protocolを利用する開発者は、たとえば、ハットのミントや移転において、onERC1155Received
への呼び出しが含まれないことに注意する必要があります。
Coreコントラクトを見ていこう!!
ではここからは Hats Protocolのコア機能が実装されているスマートコントラクトを確認していきたいと思います!!
コア機能を提供しているコントラクトは以下の4つです。
そしてインターフェース用のファイルとして以下の4つが存在します!
ここからはそれぞれ見ていこうと思います!!
Hats.sol
Hatsは、DAOに特化した取り消し可能かつプログラム可能な役割を表しており、これらは非譲渡のERC-1155に似たトークンとして実装されています。
このHats.solは、指定されたブロックチェーン上で全てのHatsを管理するマルチテナントコントラクトです。
ERC1155インターフェースを完全に実装しているものの、ERC1155標準には完全には準拠していません。
ステート変数
-
name
コントラクトの名前(通常はバージョンを含む)string public name;
-
lastTopHatId
最後に作成されたトップハットのIDの最初の4バイト
uint32 public lastTopHatId;
-
baseImageURI
画像URIが指定されていないハットトークンのためのフォールバック画像URIstring public baseImageURI;
-
_hats
ハットとハットIDの内部マッピング。
ハットIDの仕組みについては、HatsIdUtilities.solを参照mapping(uint256 => Hat) internal _hats;
-
badStandings
特定のハットの着用者が不良状態にあることを示すマッピング。
外部のコントラクトで、ペナルティを課すために使用される。mapping(uint256 => mapping(address => bool)) public badStandings;
関数
-
mintTopHat
自身が管理者であるハット、つまり「トップハット」を作成し、ミントする関数。トップハットには適格性やトグルがない。function mintTopHat( address _target, string calldata _details, string calldata _imageURI ) public returns (uint256 topHatId);
-
createHat
新しいハットを作成する関数。
msg.sender
は_admin
ハットを着用している必要がある。新しいHat構造体を初期化するが、トークンはミントされない。function createHat( uint256 _admin, string calldata _details, uint32 _maxSupply, address _eligibility, address _toggle, bool _mutable, string calldata _imageURI ) public returns (uint256 newHatId);
-
batchCreateHats
複数のハットを一括で作成する関数。
msg.sender
はそれぞれのハットの管理者である必要がある。function batchCreateHats( uint256[] calldata _admins, string[] calldata _details, uint32[] calldata _maxSupplies, address[] memory _eligibilityModules, address[] memory _toggleModules, bool[] calldata _mutables, string[] calldata _imageURIs ) public returns (bool success);
-
getNextId
次の子ハットのIDを取得する関数。lastHatIdはインクリメントされない。function getNextId(uint256 _admin) public view returns (uint256 nextId);
-
mintHat
適格な受領者にハットのERC1155に似たトークンをミントし、受領者がそのハットを「着用」する関数。function mintHat( uint256 _hatId, address _wearer ) public returns (bool success);
-
batchMintHats
複数のハットを一括でミントする関数。msg.senderはそれぞれのハットの管理者である必要がある。function batchMintHats( uint256[] calldata _hatIds, address[] calldata _wearers ) public returns (bool success);
-
setHatStatus
ハットの状態をアクティブから非アクティブ、またはその逆に切り替える関数。msg.sender
はそのハットのトグルとして設定されている必要がある。function setHatStatus( uint256 _hatId, bool _newStatus ) external returns (bool toggled);
-
checkHatStatus
ハットのトグルモジュールをチェックし、返された状態を処理する関数。ストレージ内のハットの状態を変更する可能性がある。function checkHatStatus(uint256 _hatId) public returns (bool toggled);
-
setHatWearerStatus
ハットの適格性モジュールが報告する着用者の状態を報告し、falseであればそのハットを取り消す関数。
取り消された場合、着用者のハットはバーンされる。function setHatWearerStatus( uint256 _hatId, address _wearer, bool _eligible, bool _standing ) external returns (bool updated);
-
checkHatWearerStatus
ハットの適格性モジュールに着用者の状態を報告するように要求し、falseであればそのハットを取り消す関数。取り消された場合、着用者のハットはバーンされる。
function checkHatWearerStatus( uint256 _hatId, address _wearer ) public returns (bool updated);
-
renounceHat
ハットを「放棄」する関数。msg.sender
のハットをバーンする。function renounceHat(uint256 _hatId) external;
-
transferHat
ハットを一つの着用者から他の適格な着用者へ移す関数。
ハットは可変である必要があり、管理者によって転送が開始される必要がある。function transferHat( uint256 _hatId, address _from, address _to ) public;
-
makeHatImmutable
可変ハットを不変に設定する関数。hat.configの2番目のビットを0に設定する。function makeHatImmutable(uint256 _hatId) external;
-
changeHatDetails
ハットの詳細を変更する関数。ハットは可変である必要があるが、トップハットは例外。function changeHatDetails( uint256 _hatId, string calldata _newDetails ) external;
-
changeHatEligibility
ハットの適格性モジュールを変更する関数。ハットは可変である必要がある。function changeHatEligibility( uint256 _hatId, address _newEligibility ) external;
-
changeHatToggle
ハットのトグルモジュールを変更する関数。ハットは可変である必要がある。function changeHatToggle( uint256 _hatId, address _newToggle ) external;
-
changeHatImageURI
ハットの画像URIを変更する関数。ハットは可変である必要があり、トップハットは例外。function changeHatImageURI(uint256 _hatId, string calldata _newImageURI) external;
-
changeHatMaxSupply
ハットの最大供給量を変更する関数。ハットは可変である必要があり、新しい最大供給量は現在の供給量より少なくできない。function changeHatMaxSupply( uint256 _hatId, uint32 _newMaxSupply ) external;
-
requestLinkTopHatToTree
ハットツリーを親ツリーにリンクするためのリクエストを送信する関数。function requestLinkTopHatToTree( uint32 _topHatDomain, uint256 _requestedAdminHat ) external;
-
approveLinkTopHatToTree
ハットツリーを親ツリーにリンクするリクエストを承認する関数。
リンク先の適格性やトグルモジュールを追加し、メタデータを変更するオプションがある。function approveLinkTopHatToTree( uint32 _topHatDomain, uint256 _newAdminHat, address _eligibility, address _toggle, string calldata _details, string calldata _imageURI ) external;
-
unlinkTopHatFromTree
親ツリーとのリンクを解除し、トグルと適格性を削除する関数。画像URIはbaseImageURIにリセットされる。function unlinkTopHatFromTree(uint32 _topHatDomain) external;
-
viewHat
指定されたハットIDに対応するハットのメタデータを返す関数。ハットIDの仕組みについては
HatsIdUtilities.sol
を参照。function viewHat(uint256 _hatId) public view returns ( uint256 id, uint256 admin, string memory details, uint32 maxSupply, address eligibility, address toggle, bool mutable, string memory imageURI, uint32 supply, bool active );
-
balanceOf
特定のアドレスが所有する特定のハットの数を返す関数。function balanceOf( address _wearer, uint256 _hatId ) public view override returns (uint256 balance);
-
ownerOf
現在の所有者のリストを返す関数。着用者のみを対象とし、最大20の結果を返す。ページネーションのサポートを計画。function ownerOf(uint256 _hatId) public view returns (address[] memory owners);
-
isWearerOfHat
指定されたアドレスが指定されたハットを着用しているかどうかを返す関数。function isWearerOfHat( address _wearer, uint256 _hatId ) public view returns (bool isWearer);
-
isActive
ハットがアクティブかどうかを確認する関数。function isActive(uint256 _hatId) public view returns (bool active);
-
isInGoodStanding
ハットの着用者が不良状態でないかを確認する関数。function isInGoodStanding( uint256 _hatId, address _wearer ) public view returns (bool good);
-
isAdminOfHat
指定されたアドレスが特定のハットの管理者であるかどうかを確認する関数。_hatId
の管理者が_wearer
の所有する他のハットである場合、真を返す。function isAdminOfHat( address _wearer, uint256 _hatId ) public view returns (bool isAdmin);
-
getImageURIForHat
ハットの画像URIを取得する関数。
画像URIが設定されていない場合はbaseImageURIを返す。function getImageURIForHat(uint256 _hatId) public view returns (string memory uri);
-
uri
ハットトークンのERC-1155標準準拠のURIメソッド。function uri(uint256 _hatId) public view override returns (string memory);
-
supportsInterface
特定のインターフェースをサポートしているかどうかを確認する関数。function supportsInterface(bytes4 interfaceId) public view virtual override(ERC1155) returns (bool);
-
_beforeTokenTransfer
トークンの転送前に実行される内部関数。function _beforeTokenTransfer( address operator, address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) internal virtual override;
構造体
Hatという構造体が定義されています。
struct Hat {
address eligibility; // 適格性モジュールのアドレス
uint32 maxSupply; // 最大供給量
uint32 supply; // 現在の供給量
uint16 lastHatId; // 最後のハットID
address toggle; // トグルモジュールのアドレス
uint96 config; // 構成データ
string details; // ハットの詳細
string imageURI; // ハットの画像URI
}
HatsEvents.sol
このコントラクトでは各種イベントが定義されています。
-
HatCreated
新しいハットが作成された時に発行されるイベントevent HatCreated( uint256 id, string details, uint32 maxSupply, address eligibility, address toggle, bool mutable_, string imageURI );
-
WearerStandingChanged
ハットを着用している人のステータスが更新された時に発行されるイベント適格性(Eligibility)は含まれていません。適格性の真偽は適格性モジュールによって管理され、トランザクションなしで変更されることがあるためです。
event WearerStandingChanged( uint256 hatId, address wearer, bool wearerStanding );
-
HatStatusChanged
ハットのステータスが更新された時に発行されるイベントevent HatStatusChanged(uint256 hatId, bool newStatus);
-
HatDetailsChanged
ハットの詳細が更新された時に発行されるイベントevent HatDetailsChanged(uint256 hatId, string newDetails);
-
HatEligibilityChanged
ハットの適格性モジュールが更新された時に発行されるイベントevent HatEligibilityChanged(uint256 hatId, address newEligibility);
-
HatToggleChanged
ハットのトグルモジュールが更新された時に発行されるイベントevent HatToggleChanged(uint256 hatId, address newToggle);
-
HatMutabilityChanged
ハットの可変性が更新された時に発行されるイベントevent HatMutabilityChanged(uint256 hatId);
-
HatMaxSupplyChanged
ハットの最大供給量が更新された時に発行されるイベントevent HatMaxSupplyChanged(uint256 hatId, uint32 newMaxSupply);
-
HatImageURIChanged
ハットの画像URIが更新された時に発行されるイベントevent HatImageURIChanged(uint256 hatId, string newImageURI);
-
TopHatLinkRequested
トッパーハットのリンクがその管理者によってリクエストされた時に発行されるイベントevent TopHatLinkRequested(uint32 domain, uint256 newAdmin);
-
TopHatLinked
トッパーハットが別のツリーにリンクされた時に発行されるイベントevent TopHatLinked(uint32 domain, uint256 newAdmin);
HatsErrors.sol
このファイルではHatsProtocolで利用するカスタムエラーが定義されています。
-
NotAdmin
ユーザーがhatId
に対してアクションを試みたが、そのhatId
の管理者ハットのいずれも着用していない場合に発生するエラーです。approveLinkTopHatToTree
やrelinkTopHatToTree
などの操作で発生する可能性があります。error NotAdmin(address user, uint256 hatId);
-
NotHatWearer
指定されたハットの着用者でないアカウントとして、またはそのアカウントのためにアクションを実行しようとした場合に発生するエラーです。error NotHatWearer();
-
NotAdminOrWearer
指定されたハットの管理者または着用者である必要があるアクションを試みた場合に発生するエラーです。error NotAdminOrWearer();
-
AllHatsWorn
hatId
をミントしようとしたが、そのhatId
の最大供給量が達成されている場合に発生するエラーです。error AllHatsWorn(uint256 hatId);
-
MaxLevelsReached
レベル14のハットを管理者として持つハットを作成しようとした場合に発生するエラーです。error MaxLevelsReached();
-
InvalidHatId
試みたハットIDに中間レベルが空である場合に発生するエラーです。error InvalidHatId();
-
AlreadyWearingHat
既にそのハットを着用している着用者に対して hatId をミントしようとした場合に発生するエラーです。error AlreadyWearingHat(address wearer, uint256 hatId);
-
HatDoesNotExist
存在しないハットをミントしようとした場合に発生するエラーです。error HatDoesNotExist(uint256 hatId);
-
HatNotActive
アクティブでないハットをミントまたは転送しようとした場合に発生するエラーです。error HatNotActive();
-
NotEligible
適格でない着用者に対してハットをミントまたは転送しようとした場合に発生するエラーです。error NotEligible();
-
NotHatsToggle
ハットのステータスを確認または設定しようとしたが、そのアカウントがそのハットのトグルモジュールでない場合に発生するエラーです。error NotHatsToggle();
-
NotHatsEligibility
ハットの着用者のステータスを確認または設定しようとしたが、そのアカウントがそのハットの適格性モジュールでない場合に発生するエラーです。error NotHatsEligibility();
-
BatchArrayLengthMismatch
バッチ関数に渡す配列の引数の長さが一致しない場合に発生するエラーです。error BatchArrayLengthMismatch();
-
Immutable
変更または転送が不可能なハットを試みた場合に発生するエラーです。error Immutable();
-
NewMaxSupplyTooLow
ハットの maxSupply を現在の供給量よりも低い値に変更しようとした場合に発生するエラーです。error NewMaxSupplyTooLow();
-
CircularLinkage
トッパーハットを新しい管理者にリンクしようとしたが、そのトッパーハットが新しい管理者の管理下にある場合に発生するエラーです。error CircularLinkage();
-
CrossTreeLinkage
トッパーハットを別のツリーにリンクまたは再リンクしようとした場合に発生するエラーです。error CrossTreeLinkage();
-
LinkageNotRequested
トッパーハットのリンクがリクエストなしで試みられた場合に発生するエラーです。error LinkageNotRequested();
-
InvalidUnlink
着用者が存在しないトッパーハットのリンク解除を試みた場合に発生するエラーです。これにより、リンク解除がトッパーハットを壊すことはありません。error InvalidUnlink();
-
ZeroAddress
ハットの適格性またはトグルモジュールをゼロアドレスに変更しようとした場合に発生するエラーです。error ZeroAddress();
-
StringTooLong
ハットの詳細や画像URIを7000バイト(約7000文字)を超える文字列に変更しようとした場合に発生するエラーです。これにより、管理者がハットの詳細や画像URIを長くしすぎて読み込みがブロックのガス制限を超えることを防ぎます。error StringTooLong();
HatsUtillities.sol
このコントラクトではHatsProtocolで使える便利な共通メソッドや変数が実装されています。
ステート変数
-
linkedTreeRequests
他のツリーの管理者ハットにリンクを要求するトッパーハットのマッピングリクエストが新しい管理者によって承認されるとリンクが行われます。
mapping(uint32 => uint256) public linkedTreeRequests;
-
linkedTreeAdmins
他のツリーの管理者ハットに承認されてリンクされたトッパーハットのマッピング。ハットツリーを別のツリーに接ぎ木するために使用します。ツリーはトッパーハットを介してのみ別のツリーにリンクできます。
mapping(uint32 => uint256) public linkedTreeAdmins;
-
TOPHAT_ADDRESS_SPACE
ハットIDはアドレスとして機能します。指定されたハットのIDは、そのハットツリー内での位置を表します:レベル、管理者、管理者の管理者(など、トッパーハットまで上昇)。最上位レベルは4バイトで構成され、すべてのトッパーハットを参照します。下の各レベルは16ビットで構成され、最大65,536個の子ハットを含むことができます。uint256 は4バイトのトッパーハットアドレスのスペースを持ち、((256 - 32) / 16) = 14レベルの委任をサポートし、各レベルの管理者は65,536個の異なる子ハットのスペースを持ちます。ハットツリーは単一のトッパーハットで構成され、最大14レベルの深さがあります。uint256 internal constant TOPHAT_ADDRESS_SPACE = 32;
-
LOWER_LEVEL_ADDRESS_SPACE
トッパーハットの下にある各レベルのアドレススペースのビット数です。uint256 internal constant LOWER_LEVEL_ADDRESS_SPACE = 16;
-
MAX_LEVELS
トッパーハットの下の最大レベル数、つまりツリーの最大深さです。計算式は (256 - TOPHAT_ADDRESS_SPACE) / LOWER_LEVEL_ADDRESS_SPACE です。
uint256 internal constant MAX_LEVELS = 14;
関数
-
buildHatId
指定された管理者の下に新しいハットの有効なIDを構築します。管理者がすでに MAX_LEVELS に達している場合は、リバートします。
/** * @param _admin 新しい帽子の管理者のID * @param _newHat 新しい帽子のID * @return id 構築された帽子のID */ function buildHatId( uint256 _admin, uint16 _newHat ) public pure returns (uint256 id);
-
getHatLevel
指定されたハットがそのハットツリー内でのレベルを識別します。/** * @param _hatId 調べる帽子のID * @return level 帽子のツリー内でのレベル */ function getHatLevel(uint256 _hatId) public view returns (uint32 level);
-
getLocalHatLevel
指定されたハットがそのローカルハットツリー内でのレベルを識別します。getHatLevel と似ていますが、リンクされたツリーは考慮しません。
/** * @param _hatId 調べる帽子のID * @return level ローカルツリー内での帽子のレベル */ function getLocalHatLevel(uint256 _hatId) public pure returns (uint32 level);
-
isTopHat
ハットがトッパーハットであるかどうかを確認します。/** * @param _hatId 調べる帽子のID * @return _isTopHat 帽子がトップハットであるかどうか */ function isTopHat(uint256 _hatId) public view returns (bool _isTopHat);
-
isLocalTopHat
ハットがそのローカルハットツリー内でトッパーハットであるかどうかを確認します。isTopHat と似ていますが、リンクされたツリーは考慮しません。
/** * @param _hatId 調べる帽子のID * @return _isLocalTopHat 帽子がローカルツリーのトップハットであるかどうか */ function isLocalTopHat(uint256 _hatId) public pure returns (bool _isLocalTopHat);
-
isValidHatId
ハットIDが有効かどうかを確認します。/** * @param _hatId 調べる帽子のID * @return validHatId 帽子のIDが有効であるかどうか */ function isValidHatId(uint256 _hatId) public pure returns (bool validHatId);
-
getAdminAtLevel
指定されたハットの指定レベルの管理者ハットのIDを取得します。この関数は、linkedTreeAdmin ポインタをたどって、別のツリーにあるハットを探します。
/** * @param _hatId 調べる帽子のID * @param _level 調べるレベル * @return admin 指定レベルの管理者の帽子ID */ function getAdminAtLevel( uint256 _hatId, uint32 _level ) public view returns (uint256 admin);
-
getAdminAtLocalLevel
指定されたハットがそのツリー内での指定レベルの管理者ハットのIDを取得します。/** * @param _hatId 調べる帽子のID * @param _level 調べるレベル * @return admin ローカルツリー内で指定レベルの管理者の帽子ID */ function getAdminAtLocalLevel( uint256 _hatId, uint32 _level ) public pure returns (uint256 admin);
-
getTopHatDomain
指定されたハットのトッパーハットドメインを取得します。ドメインはハットIDの最初の4バイトに格納されている、ハットツリーの識別子です。
/** * @param _hatId 調べる帽子のID * @return domain 帽子のトップハットドメイン */ function getTopHatDomain(uint256 _hatId) public pure returns (uint32 domain);
-
getTippyTopHatDomain
最も高い親トッパーハット、いわゆる「ティッピートッパーハット」のドメインを取得します。/** * @param _topHatDomain 調べるトップハットドメイン * @return domain 最高の親トップハットのドメイン */ function getTippyTopHatDomain(uint32 _topHatDomain) public view returns (uint32 domain);
-
noCircularLinkage
ツリーの循環リンクがないかをチェックします。/** * @param _topHatDomain リンクするツリーのドメイン * @param _linkedAdmin リンク先の管理者の帽子ID * @return notCircular 円環状リンクが見つからなかった場合にtrue */ function noCircularLinkage( uint32 _topHatDomain, uint256 _linkedAdmin ) public view returns (bool notCircular);
-
sameTippyTopHatDomain
トッパーハットドメインとそのポテンシャルリンク管理者が同じツリーから来ていること、つまり同じティッピートッパーハットドメインを持っていることを確認します。/** * @param _topHatDomain リンクするトップハットのドメイン * @param _newAdminHat 新しいリンク先の管理者の帽子ID * @return sameDomain トップハットドメインと新しい管理者のドメインが同じかどうか */ function sameTippyTopHatDomain( uint32 _topHatDomain, uint256 _newAdminHat ) public view returns (bool sameDomain);
SDKを見ていこう!!
Coreコントラクトを細かく確認したので次は SDK についての細かく確認していきましょう!!
公式ドキュメントでは以下のページで確認できます!!
読み取り系のメソッド
-
viewHat
帽子のプロパティを取得します。const hat = await hatsClient.viewHat(hatId);
取得できるプロパティは以下の通り
{ details: string; maxSupply: number; supply: number; eligibility: Address; toggle: Address; imageUri: string; numChildren: number; mutable: boolean; active: boolean; }
-
isWearerOfHat
特定の帽子を着用しているかどうかをチェックします。const isWearer = await hatsClient.isWearerOfHat({ wearer, hatId, })
-
isAdminOfHat
特定の帽子の管理者であるかどうかをチェックします。const isAdmin = await hatsClient.isAdminOfHat({ user, hatId, });
-
isActive
帽子がアクティブかどうかをチェックします。const isActive = await hatsClient.isActive(hatId);
-
isInGoodStanding
特定の帽子の着用者が「良好な状態」にあるかどうかをチェックします。const isGoodStanding = await hatsClient.isInGoodStanding({ wearer, hatId, });
-
isEligible
特定の帽子に対してアドレスが適格であるかどうかをチェックします。const isEligible = await hatsClient.isEligible({ wearer, hatId, });
-
predictHatId
まだ作成されていない帽子のIDを予測します。const hatId = await hatsClient.predictHatId(admin);
-
getTreesCount
存在するツリーの数を取得します。const numTrees = await hatsClient.getTreesCount();
-
getLinkageRequest
ツリーのリンクリクエストを取得します。
const requestedAdminHat = await hatsClient.getLinkageRequest(topHatDomain);
-
getLinkedTreeAdmin
リンクされたツリーの管理者を取得します(TopperHatがリンクされている帽子)。const adminHat = await hatsClient.getLinkedTreeAdmin(topHatDomain);
-
getHatLevel
帽子のレベルを取得します。ツリーがリンクされている場合、レベルはグローバルツリー(すべてのリンクツリーを含む)で計算されます。const level = await hatsClient.getHatLevel(hatId);
-
getLocalHatLevel
ローカルツリー内での帽子のレベルを取得します(リンクされたツリーを考慮しません)。const level = await hatsClient.getLocalHatLevel(hatId);
-
getTopHatDomain
帽子のツリードメインを取得します。const domain = await hatsClient.getTopHatDomain(hatId);
-
getTippyTopHatDomain
指定されたツリーが含まれるグローバルツリーのトッパーハット(「ティッピートッパーハット」)のツリードメインを取得します。const domain = await hatsClient.getTippyTopHatDomain(topHatDomain);
-
getAdmin
帽子の直接の管理者を取得します。const admin = await hatsClient.getAdmin(hatId);
-
getChildrenHats
帽子の子供の帽子を取得します。const children = await hatsClient.getChildrenHats(hatId);
書き込み系のメソッド
続いてSDKに用意されている書き込み系の処理を見ていきましょう!!
-
mintTopHat
新しいトップハットを作成する(新しいツリーを作成する)。const mintTopHatResult = await hatsClient.mintTopHat({ account, target, details, imageURI, });
-
createHat
ハットを作成する。const createHatResult = await hatsClient.createHat({ account, admin, details, maxSupply, //ハットの最大着用者数。 eligibility, // ハットの適格性アドレス(ゼロアドレスは無効)。 toggle, // ハットのトグルアドレス(ゼロアドレスは無効)。 mutable, // ハットを変更可能にする場合はtrue、そうでない場合はfalse。 imageURI, // 任意のハットの画像URI。 });
-
batchCreateHats
複数のハットを作成する。const batchCreateHatsResult = await hatsClient.batchCreateHats({ account, admins, details, maxSupplies, eligibilityModules, toggleModules, mutables, imageURIs, });
-
ハットをミントする
const mintHatResult = await hatsClient.mintHat({ account, hatId, wearer, });
-
batchMintHats
複数のハットをミント(発行)する。const batchMintHatsResult = await hatsClient.batchMintHats({ account, hatIds, wearers, });
-
setHatStatus
ハットのステータスをアクティブ/非アクティブに設定する。const setHatStatusResult = await hatsClient.setHatStatus({ account, hatId, newStatus, });
-
checkHatStatus
トグルモジュールを呼び出してハットのステータスを確認し、必要に応じてステータスを更新する。const checkHatStatusResult = await hatsClient.checkHatStatus({ account, hatId, });
-
setHatWearerStatus
ハットの着用者のステータスを設定する。const setHatWearerStatusResult = await hatsClient.setHatWearerStatus({ account, hatId, wearer, eligible, standing, });
-
checkHatWearerStatus
ハットの着用者のステータスを確認し、必要に応じて更新する。const checkHatWearerStatusResult = await hatsClient.checkHatWearerStatus({ account, hatId, wearer, });
-
transferHat
ハットを別のユーザーに譲渡する。const transferHatResult = await hatsClient.transferHat({ account, hatId, from, to, });
-
renounceHat
ユーザーが自分のハットを放棄する。const renounceHatResult = await hatsClient.renounceHat({ account, hatId, wearer, });
-
changeHatDetails
ハットの詳細を変更する。const changeHatDetailsResult = await hatsClient.changeHatDetails({ account, hatId, newDetails, });
-
changeHatEligibility
ハットの適格性モジュールを変更する。const changeHatEligibilityResult = await hatsClient.changeHatEligibility({ account, hatId, newEligibility, });
-
changeHatToggle
ハットのトグルモジュールを変更する。const changeHatToggleResult = await hatsClient.changeHatToggle({ account, hatId, newToggle, });
-
changeHatMutable
ハットの変更可能フラグを更新する。const changeHatMutableResult = await hatsClient.changeHatMutable({ account, hatId, newMutable, });
-
changeHatImageURI
ハットの画像URIを更新する。const changeHatImageURIResult = await hatsClient.changeHatImageURI({ account, hatId, newImageURI, });
-
changeHatName
ハットの名前を変更する。const changeHatNameResult = await hatsClient.changeHatName({ account, hatId, newName, });
-
setHatStatus
ハットのステータスを直接設定する。const setHatStatusResult = await hatsClient.setHatStatus({ account, hatId, wearer, eligible, standing, });
-
revokeHatWearer
特定の着用者からハットを取り上げる。const renounceHatResult = await hatsClient.renounceHat({ account, hatId, });
MuitiCall
一括で複数のトランザクションを実行することもできます!!
const multicallResult = await hatsClient.multicall({
account,
calls,
});
calls
に実行させたいトランザクションのデータを詰めます。
{
account: Account | Address;
calls: {
functionName: string;
callData: Hex;
}[];
}
例えば以下のように用意します。
const mintTopHatCallData = await hatsClient.mintTopHatCallData({
target,
details,
imageURI,
});
const createHatCallData = await hatsClient.createHatCallData({
admin,
details,
maxSupply,
eligibility,
toggle,
mutable,
imageURI,
});
const mintHatCallData = await hatsClient.mintHatCallData({
hatId,
wearer,
});
const multiCallResult = await hatsClient.multicall({
account,
calls: [
mintTopHatCallData,
createHatCallData,
mintHatCallData
],
});
multicallPreFlightCheck
というマルチコール用のシミュレート関数があります。
await hatsClient.multicallPreFlightCheck({
account,
calls,
});
ClaimHat
Hat関連で便利な関数がいくつか用意されています。
-
accountCanClaim
アカウントが特定のハットを請求できるかどうかを確認します。const canClaim = await hatsClient.accountCanClaim({ hatId, account, });
-
canClaimForAccount
特定のアカウントの代わりにハットを請求できるかどうかを確認します。const canClaimFor = await hatsClient.canClaimForAccount({ hatId, account, });
-
claimHat
呼び出し元のアカウントのためにハットを請求します。const claimHatResult = await hatsClient.claimHat({ account, hatId, });
-
claimHatFor
選択されたアカウントの代わりにハットを請求します。const claimHatForResult = await hatsClient.claimHatFor({ account, hatId, wearer, });
-
multiClaimHatFor
複数のアカウントの代わりにハットを請求します。const claimHatForResult = await hatsClient.multiClaimHatFor({ account, hatId, wearers, });
便利な機能
他にもSDKには便利な機能がいくつか用意されています!!
-
hatIdDecimalToHex
ハットIDを10進数から16進数に変換します。import { hatIdDecimalToHex } from "@hatsprotocol/sdk-v1-core"; const hatIdHex = hatIdDecimalToHex(hatId);
-
hatIdHexToDecimal
ハットIDを16進数から10進数に変換します。import { hatIdHexToDecimal } from "@hatsprotocol/sdk-v1-core"; const hatIdDecimal = hatIdHexToDecimal(hatId);
-
treeIdDecimalToHex
ツリーIDを10進数から16進数に変換します。ツリーIDはハットIDの最初の4バイトです。import { treeIdDecimalToHex } from "@hatsprotocol/sdk-v1-core"; const treeIdHex = treeIdDecimalToHex(treeId);
-
treeIdHexToDecimal
ツリーIDを16進数から10進数に変換します。ツリーIDはハットIDの最初の4バイトです。import { treeIdHexToDecimal } from "@hatsprotocol/sdk-v1-core"; const treeIdDecimal = treeIdHexToDecimal(treeId);
-
treeIdToTopHatId
ツリーIDをトップハットIDに変換します。ツリーIDはハットIDの最初の4バイトです。import { treeIdToTopHatId } from "@hatsprotocol/sdk-v1-core"; const tophatId = treeIdToTopHatId(treeId);
-
hatIdToTreeId
ハットIDをツリーIDに変換します。ツリーIDはハットIDの最初の4バイトです。import { hatIdToTreeId } from "@hatsprotocol/sdk-v1-core"; const treeId = hatIdToTreeId(hatId);
-
hatIdDecimalToIp
IP形式は、見栄えの良いハットID形式として使用されることがあります。例えば、16進数IDが
0x00000001000a0002000000000000000000000000000000000000000000000000
であるハットは、IP形式で 1.10.2 となります。各レベルはドットで区切られ、ゼロを除いて10進数として表示されます。import { hatIdDecimalToIp } from "@hatsprotocol/sdk-v1-core"; const hatIdIp = hatIdDecimalToIp(hatId);
-
hatIdIpToDecimal
ハットIDをIP形式から10進数形式に変換します。import { hatIdIpToDecimal } from "@hatsprotocol/sdk-v1-core"; const hatIdDecimal = hatIdIpToDecimal(hatId);
Subgraph用のSDKについて
HatsProtocolでは SubGraphも用意されています!!
既に用意されているサブグラフのエンドポイントは以下の通りです!!
詳しい使い方は以下のドキュメントから確認ができます!!
Sepolia の場合は以下がエンドポイントのようですね
Hatsや着用者、ツリー情報の情報を取得することが可能みたいです。
取得できるデータの種類については、下記サイトで確認ができます!!
-
SDKインスタンスの生成例
import {HatsSubgraphClient} from "@hatsprotocol/sdk-v1-subgraph"; import {optimism, sepolia} from "viem/chains"; // Subgraph用のインスタンスを生成 const hatsSubgraphClient = new HatsSubgraphClient({ config: { [sepolia.id]: { endpoint: "https://api.studio.thegraph.com/query/55784/hats-v1-sepolia/version/latest", }, [optimism.id]: { endpoint: "https://api.studio.thegraph.com/query/55784/hats-v1-optimism/version/latest", }, }, });
-
SDKインスタンスの使用例
上記で生成したインスタンスは次のように使うことができます!
// hatの情報を取得する。 const hat = await hatsSubgraphClient.getHat({ chainId: 10, // optimism hatId: BigInt( "0x0000000100020001000100000000000000000000000000000000000000000000" ), props: { maxSupply: true, // get the maximum amount of wearers for the hat wearers: { // get the hat's wearers props: {}, // for each wearer, include only its ID (address) }, events: { // get the hat's events props: { transactionID: true, // for each event, include the transaction ID }, filters: { first: 10, // fetch only the latest 10 events }, }, }, }); console.log("hat:", hat);
Hats モジュール
Hatsモジュールとは、Eligibilityモジュール、Toggleモジュール、またはHatterコントラクトとして機能する任意のコントラクトを指します。
モジュールは、Hats Protocolの動作をカスタマイズ、自動化、拡張するもので、他のプロトコルやアプリケーションとのアダプターや統合ポイントとしても機能します。
ある意味で、モジュールはHats Protocolの生命線です。
デザインの幅は広く、組織や協調のためのあらゆる構成要素を作り出す可能性に満ちています。
開発者がHatsモジュールを扱うには、主に二つの方法があります。
-
Modules SDK を使います!!
-
必要なライブラリをインストールする方法
yarn add @hatsprotocol/modules-sdk viem
-
セットアップ
以下のようにセットアップするようです。
Subgraph用のSDKとほぼ同じですね!
import { HatsModulesClient } from "@hatsprotocol/modules-sdk"; const hatsModulesClient = new HatsModulesClient({ publicClient, walletClient, });
-
-
prepare
この関数は、モジュールレジストリからデータを取得します。
https://github.com/Hats-Protocol/modules-registry
このステップは、クライアントを使用できるようにするために必要です。
さらに、この関数はオプションでレジストリを入力として受け取ることができ、ユーザーのキャッシュをサポートします。もしレジストリが提供された場合、クライアントはレジストリからデータを取得する代わりに、提供されたモジュールを使用します。
await hatsModulesClient.prepare();
-
getModules
使用可能なモジュール一覧を取得するメソッド
const modules = hatsModulesClient.getAllModules();
-
getModuleById
モジュールIDを指定してモジュールを取得するメソッド
const module = hatsModulesClient.getModuleById(moduleId);
-
getModuleByImplementation
implementation
アドレスを指定してモジュールを取得するメソッドconst module = hatsModulesClient.getModuleByImplementation(address);
-
getModuleByInstance
こちらもアドレスを取得してモジュールを取得するメソッドらしい
const module = await hatsModulesClient.getModuleByInstance(address);
-
getModulesByInstances
const modules = await hatsModulesClient.getModulesByInstances(addresses);
-
createNewInstance
新しいモジュールインスタンスを作成します。
/** * * @param {Account | Address} account - Viemアカウント(JSON-RPCアカウントの場合はアドレス、その他のタイプの場合はアカウント)。 * @param {string} moduleId - モジュールID(実装アドレス)。 * @param {bigint} hatId - モジュールが作成される帽子ID。 * @param {unknown[]} immutableArgs - モジュールの不変引数。引数はModuleオブジェクト内の順序と同じでなければなりません。 * @param {unknown[]} mutableArgs - モジュールの可変引数。引数はModuleオブジェクト内の順序と同じでなければなりません。 * @param {bigint} [saltNonce] - オプションのソルトナンス。提供されない場合はランダムに生成されます。 * @return {Object} - レスポンスオブジェクト。 * @return {string} status - トランザクションが成功した場合は"success"、リバートした場合は"reverted"。 * @return {string} transactionHash - トランザクションのハッシュ。 * @return {string} newInstance - 成功した場合、新しいモジュールインスタンスのアドレス。 */ const createInstanceResult = await hatsModulesClient.createNewInstan( { account, moduleId, hatId, immutableArgs, mutableArgs, saltNonce, });
-
batchCreateNewInstances
まとめて複数のモジュールを作るメソッドです。
/** * * @param {Account | Address} account - Viemアカウント(JSON-RPCアカウントの場合はアドレス、その他のタイプの場合はアカウント)。 * @param {string[]} moduleIds - モジュールID(実装アドレス)。 * @param {bigint[]} hatIds - モジュールが作成される帽子ID。 * @param {unknown[][]} immutableArgsArray - 各モジュールの不変引数。モジュールごとに、引数はModuleオブジェクト内の順序と同じでなければなりません。 * @param {unknown[][]} mutableArgsArray - 各モジュールの可変引数。モジュールごとに、引数はModuleオブジェクト内の順序と同じでなければなりません。 * @param {bigint[]} [saltNonces] - オプションのソルトナンス。提供されない場合はランダムに生成されます。 * @return {Object} - レスポンスオブジェクト。 * @return {string} status - トランザクションが成功した場合は"success"、リバートした場合は"reverted"。 * @return {string} transactionHash - トランザクションのハッシュ。 * @return {string[]} newInstances - 成功した場合、新しいモジュールインスタンスのアドレス。 */ const createInstancesResult = await hatsModulesClient.batchCreateNewInstances( { account, moduleIds, hatIds, immutableArgsArray, mutableArgsArray, saltNonces });
-
predictHatsModuleAddress
作成引数を使用して、モジュールのアドレスを予測します。
/** * * @param {string} moduleId - モジュールID。 * @param {bigint} hatId - インスタンス作成関数に提供されたターゲットの帽子ID。 * @param {unknown[]} immutableArgs - インスタンス作成関数に提供されたモジュールの不変引数。 * @param {bigint} saltNonce - 使用するソルトナンス。 * @return {string} - 予測されたモジュールアドレス。 */ const predictedAddress = await hatsModulesClient.predictHatsModuleAddress( { moduleId, hatId, immutableArgs, saltNonce, });
-
createEligibilitiesChain
新しい適格性モジュールを作成するメソッド
/** * @param {Account | Address} account - Viemアカウント(JSON-RPCアカウントの場合はAddress、それ以外はAccount)。 * @param {bigint} hatId - モジュールが作成される対象の帽子ID。 * @param {number} numClauses - 論理結合節の数。 * @param {number[]} clausesLengths - 各節の長さ。 * @param {string[]} modules - 提供された各節に対応する順序でチェーン化するモジュールインスタンスの配列。 * @param {bigint} [saltNonce] - オプションのソルトナンス。指定されない場合はランダムに生成されます。 * @return {Object} - トランザクション結果と新しいチェーンモジュールインスタンスのアドレス。 */ const createInstanceResult = await hatsModulesClient.createEligibilitiesChain( { account, hatId, numClauses, clausesLengths, modules, saltNonce, });
-
createTogglesChain
新しいトグルモジュールを作成するメソッド
/** * @param {Account | Address} account - Viemアカウント(JSON-RPCアカウントの場合はAddress、それ以外はAccount)。 * @param {bigint} hatId - モジュールが作成される対象の帽子ID。 * @param {number} numClauses - 論理結合節の数。 * @param {number[]} clausesLengths - 各節の長さ。 * @param {string[]} modules - 提供された各節に対応する順序でチェーン化するモジュールインスタンスの配列。 * @param {bigint} [saltNonce] - オプションのソルトナンス。指定されない場合はランダムに生成されます。 * @return {Object} - トランザクション結果と新しいチェーンモジュールインスタンスのアドレス。 */ const createInstanceResult = await hatsModulesClient.createTogglesChain( { account, hatId, numClauses, clausesLengths, modules, saltNonce, });
-
getRulesets
モジュールインスタンスのルールセットを取得するメソッド
/** * @param {string} address - インスタンスのアドレス。 * @return {Ruleset[] | undefined} - モジュールのルールセット、または指定されたアドレスがモジュールでない場合はundefined。 */ const rulesets = await hatsModulesClient.getRulesets(address);
-
getRulesetsBatched
複数のモジュールインスタンスのルールセットを取得するメソッド
/** * @param {string[]} addresses - インスタンスのアドレスの配列。 * @return {(Ruleset[] | undefined)[]} - 各モジュールインスタンスのルールセット、または指定されたアドレスがモジュールでない場合はundefined。 */ const rulesets = await hatsModulesClient.getRulesetsBatched(addresses);
-
isChain
モジュールインスタンスがモジュールチェーンかどうかを確認するメソッド
/** * @param {string} address - インスタンスのアドレス。 * @return {boolean} - インスタンスがチェーンであればtrue、そうでなければfalse。 */ const isChain = await hatsModulesClient.isChain(address);
-
isChainBatched
複数のモジュールインスタンスがモジュールチェーンかどうかを確認するメソッド
/** * @param {string[]} addresses - インスタンスのアドレスの配列。 * @return {boolean[]} - 各インスタンスがチェーンであればtrue、そうでなければfalse。 */ const isChainBatched = await hatsModulesClient.isChainBatched(addresses);
-
isModuleDeployed
モジュールが既にデプロイされているかどうかを確認するメソッド
/** * @param {string} moduleId - モジュールのID。 * @param {bigint} hatId - インスタンス作成関数に提供されたターゲットの帽子ID。 * @param {unknown[]} immutableArgs - インスタンス作成関数に提供されたモジュールの不変引数。 * @param {bigint} saltNonce - 使用するソルトナンス。 * @return {boolean} - モジュールがデプロイされていれば true、そうでなければ false。 */ const isDeployed = await hatsModulesClient.isModuleDeployed( { moduleId, hatId, immutableArgs, saltNonce, });
-
getInstanceParameters
モジュールインスタンスのライブパラメータを取得するメソッド
/** * @param {string} instance - インスタンスのアドレス。 * @return {object[]} - 各パラメータの情報を含むオブジェクトの配列 */ const module = await hatsModulesClient.getInstanceParameters(instance);
-
callInstanceWriteFunction
モジュールの書き込み関数を呼び出すメソッド。
モジュールのオブジェクトの
customRoles
とwriteFunctions
プロパティを使用して、モジュールのすべての書き込み関数をプログラム的に取得し、各関数を呼び出すために必要な入力引数やロール(Hats)を取得します。/** * @param {Account | Address} account - Viemアカウント(JSON-RPCアカウント用のAddressまたはその他のタイプのAccount)。 * @param {string} moduleId - モジュールのID(実装アドレス)。 * @param {Address} instance - インスタンスのアドレス。 * @param {WriteFunction} func - 呼び出す関数をWriteFunctionタイプのオブジェクトとして提供します。 * @param {unknown[]} args - 関数に渡す入力引数。 */ const res = await hatsModulesClient.callInstanceWriteFunction( { account, moduleId, instance, func, args, });
-
-
-
下記を参考にして新しくモジュールを作ることになります!!
https://docs.hatsprotocol.xyz/for-developers/hats-modules/building-hats-modules
-
Hatsモジュールの内部構造
通常、HatsモジュールはHatsModule.solコントラクトを継承します。
https://github.com/Hats-Protocol/hats-module/blob/main/src/HatsModule.sol
-
HotModule.sol
- 新しいHatsモジュールコントラクトに必要な基本的なテンプレートを提供します。
- HatsModuleFactory.solとの互換性を持たせます。これにより、ユーザーは指定されたモジュールの新しいインスタンスを最も簡単かつ安価にデプロイできます。
- モジュールレジストリとの互換性を持たせ、ユーザーがモジュールを見つけやすくし、Hatsのフロントエンドに統合することができます。
これは抽象コントラクトであり、特定のユースケースに対応するためにモジュールが拡張できる汎用的な構造を提供します。
HatsModule.solを継承することは、HatsModuleFactoryを介してデプロイ可能であること、モジュールレジストリにリストされること、レジストリを使用するアプリケーションにネイティブに表示されるために必要です。
ただし、Hats Protocol全般と互換性を持たせるためには必須ではありません。これに関する要件については、EligibilityモジュールとToggleモジュールのドキュメントを参照してください。
-
HotModule.solの主な機能
HatsModule.solの主な機能は、モジュールをHatsModuleFactoryを介して構成しデプロイできるようにすることです。通常、モジュールが関連付けられる新しいハットごとに、そのモジュールの新しいインスタンスが必要になるため、デプロイの際にガス効率が重要です。さらに、モジュールはその生涯の間に何度も呼び出されることが多いため、実行時のガス効率も重要です。
これらの理由から、HatsModule.solは、イミュータブルな引数をサポートする最小限のプロキシ(クローン)コントラクトとして構成されています。これはEIP-1167標準に類似しており、ガス効率の高いLibClone.solライブラリを実装しています。
HatsModule.solの動作に関する詳細は、以下のページを参照してください。
-
拡張機能
HatsModule.solには、一般的なモジュールタイプの出発点として役立ついくつかの標準的な拡張機能があります。
- HatsEligibilityModule.sol: IHatsEligibility.solを実装
- HatsToggleModule.sol: IHatsToggle.solを実装
-
-
新しいモジュールを作る方法
-
はじめに
hats-module-templateリポジトリを使えば、新しいモジュールの構築を簡単に始められます。このリポジトリには、次の内容が含まれています:- Hatsに関連する設定がされた初期化済みのFoundryプロジェクト
- 初期依存関係として追加されたforge-stdとhats-module
- スタート用のモジュールコントラクトのひな形
- テストとデプロイ用のファイルテンプレート
- Forgeテストとガスコストの差分を確認するためのGithub CIワークフロー
エリジビリティモジュールを作成するには、HatsModuleを継承し、さらにIHatsEligibilityインターフェースを実装するHatsEligibilityModuleコントラクトをインポートして継承します:
import { HatsEligibilityModule } from "hats-module/HatsModule.sol";
同様に、トグルモジュールの場合は、HatsModuleを継承し、さらにIHatsToggleインターフェースを実装するHatsToggleModuleコントラクトをインポートして継承します:
import { HatsToggleModule } from "hats-module/HatsModule.sol";
-
モジュールインスタンスのデプロイ方法
HatsModuleを継承したすべてのコントラクトは、HatsModuleFactoryを介して最小限のプロキシクローンとしてデプロイできます。HatsModuleFactoryでは、次の操作が可能です。
- 単一のモジュールインスタンスを作成
- 複数のモジュールインスタンスを一括作成
- モジュールインスタンスのアドレスを予測
- モジュールインスタンスが既にデプロイされているかを確認
これらの関数はHatsModuleFactoryコントラクトで直接呼び出すことができ、またModules SDKを介して簡単に新しいモジュールインスタンスをデプロイすることもできます。
-
createHatsModule
function createHatsModule( address _implementation, uint256 _hatId, bytes calldata _otherImmutableArgs, bytes calldata _initData ) public returns (address _instance)
新しいモジュールのデプロイとセットアップが完了すると、次のイベントが発行され、新しいインスタンスのアドレスが含まれます。
event HatsModuleFactory_ModuleDeployed( address implementation, address instance, uint256 hatId, bytes otherImmutableArgs, bytes initData );
-
batchCreateHatsModule
batchCreateHatsModule関数は、createHatsModule関数を使用して複数のインスタンスを作成します。
必要な作成パラメータは配列として渡され、それぞれの配列の長さは作成するモジュールの数と同じである必要があります。各モジュールに対して、createHatsModule関数が同じインデックスの配列エントリで呼び出されます。
function batchCreateHatsModule( address[] calldata _implementations, uint256[] calldata _hatIds, bytes[] calldata _otherImmutableArgsArray, bytes[] calldata _initDataArray ) public returns (bool success)
-
getHatsModuleAddress
getHatsModuleAddress関数は、モジュールインスタンスが作成される前に、そのアドレスを予測します。
function getHatsModuleAddress( address _implementation, uint256 _hatId, bytes calldata _otherImmutableArgs ) public view returns (address)
-
deployed
deployed関数は、特定のモジュールインスタンスがデプロイされているかどうかを確認します。
function deployed( address _implementation, uint256 _hatId, bytes calldata _otherImmutableArgs ) public view returns (bool)
-
HatsModuleFactoryのアドレス
どのネットワークでもアドレスは共通して 0xfE661c01891172046feE16D3a57c3Cf456729efA みたいです。
https://sepolia.etherscan.io/address/0xfe661c01891172046fee16d3a57c3cf456729efa
-
-
Module Chainsについて
エリジビリティ条件を複数のモジュールで組み合わせたい場合はどうすればいいでしょうか?例えば、ある人が特定のNFT(例:DAOのメンバーシップを表すもの)を持っていることと、さらに選挙で勝利することが条件である場合です。これこそが、エリジビリティチェーン/トグルチェーンモジュールの目的です。
HatsEligibilitiesChainは、複数のエリジビリティモジュールを「and」/「or」の論理演算で組み合わせるエリジビリティモジュールです。同様に、HatsTogglesChainは、複数のトグルモジュールを組み合わせるトグルモジュールです。
これらのモジュールは、いずれも同様の構造を持っています。モジュールは、「and」演算子で結合された論理節の非結合形式でチェーン化されます。例えば、"(module1 && module2) || module3"には、2つの論理節があります。
- "(module1 && module2)"
- "module3"
これらの論理節は、「or」演算子でチェーン化されています。
-
Deriving Wearer Eligibility:
エリジビリティチェーンモジュールでは、各モジュールのエリジビリティをチェックし、選択された論理演算に基づいて結果を組み合わせることで、着用者のエリジビリティが判定されます。しかし、どのモジュールにおいても着用者が「不良ステータス」にある場合、そのモジュールは「エリジブルではない」および「不良ステータスにある」という結果を返します。
-
Deriving Hat Status:
トグルチェーンモジュールでは、各モジュールのステータスをチェックし、選択された論理演算に基づいて結果を組み合わせることで、ハットのステータスが判定されます。
-
新しいエリジビリティ/トグルチェーンモジュールを作成する
このモジュールは、可変ストレージ変数を使用せず、初期化データも使用しません。以下の不変変数のみが使用され、モジュールインスタンスの作成時に設定されます。
- 論理節の数
- 論理節の長さ
- エリジビリティ/トグルモジュールのリスト
上記の例を使用して、モジュールのデプロイに使用される不変引数の例は次のとおりです。
bytes memory otherImmutableArgs = abi.encodePacked(2, [2,1], module1Address, module2Address, module3Address);
このモジュールには、これらの不変変数のための以下のゲッターが含まれています。
function NUM_CONJUCTION_CLAUSES() public pure returns (uint256) function CONJUCTION_CLAUSE_LENGTHS() public pure returns (uint256[] memory) function MODULES() public pure returns (address[] memory)
-
今回はここまでになります!!
読んでいただきありがとうございました!!
Discussion