手動運用 Runbook: 申請の承認・Allowlist 追加・HENKAKU 配布
このRunbookはフェーズ1のMVP-1で、申請を確認してからAllowlist追加とHENKAKU配布を行うための手順です。アプリは申請と状態・監査記録を管理しますが、オンチェーン操作は自動実行しません。
役割
- 承認者:
ADMIN_ADDRESSESに登録されたウォレットの保持者。申請内容を確認し、/adminで審査状態を更新する - Allowlist 操作者: Allowlistを管理するコントラクトまたは管理画面を操作できる担当者
- 配布実行者: Safe Walletの承認・実行権限を持つ担当者。承認済み申請にのみHENKAKUを送付する
同じ人が複数の役割を担ってもよいですが、役割と確認責任は分けて扱います。秘密鍵やSafeの署名情報は、このアプリやリポジトリに置きません。
事前確認
.env.localまたはデプロイ先の環境変数に、管理者ウォレットをADMIN_ADDRESSES(カンマ区切り)で設定するADMIN_ADDRESSESの変更後は開発サーバーまたはデプロイを再起動するSUPABASE_URLとSUPABASE_SERVICE_ROLE_KEYが対象環境を指していることを確認する- 配布実行者はSafe WalletでPolygonネットワークと送付先・数量を確認できる状態にする
運用入力
Issue #48 で確認した正式な実行元と数量です(決定記録)。ここに書いてあるのはすべて公開情報で、秘密鍵・署名者と実名の対応・リカバリ情報は含めません。値を変更するときは決定記録を追加してからこの表を更新します。
| 項目 | 値 |
|---|---|
| ネットワーク | Polygon(chain ID 137) |
| HENKAKUトークンコントラクト | 0x0cc91a5FFC2E9370eC565Ab42ECE33bbC08C11a2(henkaku-v2 HenkakuToken.sol) |
| Allowlist追加の実行元 Safe | 0x3F1D35bF5f182D7b4ef95a3f298f059BC33d2020(コントラクトの gateKeeper) |
| Allowlist追加の呼び出し | 上記トークンコントラクトの addWhitelistUser(address user)(onlyAdmin: owner / gateKeeper / dev のみ実行できる) |
| HENKAKU配布の実行元 Safe | 0x1C9D58eBd2A9F4952C2A4a8f9906FeF133056a33 |
| 配布数量 | 申請者1名あたり 10 HENKAKU(18 decimals。最小単位では 10000000000000000000) |
| 参考トランザクション | Allowlist追加: 0x8b70…1fe9 / 配布: 0x7be6…1eaf7(数量は今回と異なる。配布方法の参考) |
Allowlist は HENKAKU トークンコントラクト内の whitelist で管理されており、別の Allowlist コントラクトや管理画面はありません。追加・配布とも Safe Wallet(https://app.safe.global/)で Polygon を選び、手動でトランザクションを作成します。
日次フロー
1. 申請を確認する
- 承認者が管理者ウォレットでサインインし、
/adminを開く pendingまたはneeds_infoの申請を選ぶ- 申請者の
/initiationの回答・クエスト完了状況と、申請先ウォレットを確認する - 申請行に表示されるDiscord名で、Discord上の本人を確認する。アプリはDiscordを見ていないため、実在とユーザー名の一致は承認者が突き合わせる(Issue #117)
- 判断に必要な情報が足りない場合は理由欄に不足している情報を書いて「要追加情報」を選び、Discordでも申請者に確認する
2. 審査を更新する
- 条件を満たす場合は「承認」を選ぶ。承認に理由の入力は不要
- 条件を満たさない場合は、理由欄に理由を書いてから「却下」を選ぶ
- 承認前にAllowlistや配布の操作を行わない
却下と要追加情報の理由は必須で、入力した文面がそのまま /apply の申請者に表示されます。 申請者が次に何をすればよいか分かる書き方にしてください(「運営判断」のような文面は申請者に何も伝えません)。理由欄には、判断に必要のない個人情報や秘密情報を記載しないでください。理由は実行者・時刻・変更前後の状態とともに application_events に記録され、/admin の該当行にも表示されます。
承認後、画面を再読み込みしてAllowlistの操作が表示されることを確認します。配布の操作は、Allowlistを追加済みにするまで表示されません(Issue #112)。
3. Allowlistへ追加する
/adminで承認済み申請のウォレットアドレスをコピーする- Safe Wallet で Allowlist追加の実行元 Safe(
0x3F1D…2020)を Polygon で開き、Transaction Builder でトークンコントラクト0x0cc9…11a2のaddWhitelistUserを選び、userに申請者のアドレスを貼り付ける - 実行前に次を二者で照合する(確認項目)
- ネットワークが Polygon である
- 宛先コントラクトが上記のトークンコントラクトと一致する
userが/adminの申請行のアドレスと全桁一致する(先頭と末尾だけで判断しない)- 実行元が Allowlist追加の Safe である(配布 Safe と取り違えない)
- Safe の承認フローに従って署名・実行し、Polygonscan で成功(Status: Success)を確認する
/adminに戻り、成功したトランザクション hash を「Allowlist tx hash」に入力して「Allowlist 追加済みにする」を選ぶ
tx hashの入力は必須です。このアプリはAllowlist追加を実行しません。入力するtx hashは、実行済みトランザクションの確認記録です。
追加後は申請者が /setup または /passport の WALLET STATUS でオンチェーンの登録状況を確認できます(Issue #91)。
4. Safe WalletからHENKAKUを送付する
Allowlist追加と配布は直列です。Allowlist に未登録のアドレスへの転送はコントラクト側で失敗するため、必ず手順3の完了後に行います。
/adminで対象申請の Allowlist がaddedで、tx hash が記録されていることを確認する- Safe Wallet で配布の実行元 Safe(
0x1C9D…6a33)を Polygon で開き、「Send tokens」で HENKAKU を選び、宛先に申請者のアドレス、数量に10を入力する - 実行前に次を二者で照合する(確認項目)
- ネットワークが Polygon で、トークンが上記のコントラクトアドレスの HENKAKU である(同名の別トークンを選んでいない)
- 宛先が
/adminの申請行のアドレスと全桁一致し、手順3で Allowlist に追加したアドレスと同じである - 数量が 10 HENKAKU である(単位を wei にしていない)
- 同じ申請へ送付済みでない(
/adminの配布状態と履歴を確認する)
- Safe の承認フローに従って署名・実行し、Polygonscan で成功と
Transferの宛先・数量を確認する - 成功したトランザクション hash を
/adminの「配布 tx hash」に入力し、「配布済みにする」を選ぶ - 申請者の
/passportで「承認済み / 追加済み / 送付済み (tx)」が表示されることを確認し、申請行のDiscord名を宛先に申請者へ完了を連絡する
このアプリはSafeの署名・送付を実行しません。画面に入力するtx hashは、実行済みトランザクションの確認記録です。
5. 失敗・再試行
- オンチェーン操作が失敗した場合は、tx hash・エラー内容・再試行日時を運用ログに残し、原因を解消してから再試行する
/adminで「Allowlist 失敗として記録」または「配布 失敗として記録」を選び、失敗理由を入力する。理由は必須で、実行者・時刻・変更前後の状態とともにapplication_eventsに記録される- 失敗として記録した後も、原因を解消すれば「Allowlist 追加済みにする」「配布済みにする」で完了状態へ進められる
- 再試行がまた失敗した場合は、そのつど同じ操作で記録する。 完了状態(
added/sent)に達するまで、失敗は何度でも記録でき、そのたびに理由付きでapplication_eventsに1行残る。「1回目はガス不足、2回目はnonce競合」といった経緯がアプリ内に揃うため、運用ログ側に経緯を書き写す必要はない - 同じ失敗を二重に記録しないよう、ボタンは1回だけ押す。同じ内容で2回押すと監査ログに同じ行が2件残る(時刻で見分けられるが、読み手には紛らわしい)
- 申請者の
/applyでは、失敗は「運営が対応中です」と表示され、失敗の詳細は出さない - 送付済みの申請を再送付しない。再送が必要な場合は、承認者と配布実行者が既存txと残高を確認してから判断する
暫定の判断基準
コミュニティで正式な承認基準が決まるまで、次を暫定ルールとします。
- Initiationの全ステップが完了している
- Discord名が登録されている(アプリが申請の条件として確認済み)。承認者がDiscord上で本人を確認できる
- 申請者の回答に、運営が確認すべき個人情報・秘密情報が含まれていない
- 承認者2名が申請内容と送付先ウォレットを確認し、合意している
- 判断に迷う場合は承認せず「要追加情報」とし、コミュニティで確認する
最終的な確認者、承認人数、却下基準、Allowlist追加の条件はコミュニティで決定し、決定後にこの節を更新します。
権限と鍵
ADMIN_ADDRESSESは管理画面へのアクセス制御だけに使い、配布権限を与えるものではない- Safe Walletの署名者・秘密鍵・リカバリ情報はアプリ、リポジトリ、Issue、運用ログに記録しない
SESSION_PASSWORD、Supabaseキー、Safeの認証情報は環境変数またはSafeが定める安全な保管場所で管理するADMIN_ADDRESSESの変更は、変更理由を運用ログに残してから環境変数を更新し、再デプロイまたは再起動するSESSION_PASSWORDの変更は、サインイン済みのメンバー全員を即座にサインアウトさせる。 セッションはこの値で暗号化されているため、鍵が変わると復号できなくなる。変更する場合は、全員が再度ウォレットで署名し直す必要があることを事前に告知し、変更理由を運用ログに残す- セッションの有効期限は14日で、サインインした時点からの絶対期限(閲覧では延長されない)。運営メンバーのセッションも同じ扱い
記録
- 状態変更は
application_eventsに実行者アドレス、時刻、変更前後の状態、理由、tx hash(Allowlist追加・配布のいずれも)が自動記録される- 記録は監査証跡として追記のみで扱い、既存の行を編集・削除しない
- 記録は状態更新と同一トランザクションで行われるため、「状態は変わったのに記録が残っていない」状態は発生しない。 記録に失敗した場合は状態変更ごと巻き戻り、操作は適用されない
- 逆に言えば、
applicationsの状態とapplication_eventsの記録が食い違っていた場合、それはアプリ経由ではない直接のDB書き込みを疑う根拠になる - 記録は
/adminの各申請行の「履歴」を開いて確認する。実行者・時刻・変更前後の状態・理由・tx hashが新しい順に並ぶ。確認のためにDBへ直接繋ぐ必要はない
- Allowlist操作のオンチェーンtx hashは
/adminで入力し、application_eventsに残す。運用ログ側へ書き写す必要はない - 申請者への連絡や、アプリ外で行った特記事項は下記の運用ログに追記する
運用ログ
| 日時(UTC) | 申請ID | 操作 | 実行者の役割 | tx hash / 参照 | 備考 |
|---|---|---|---|---|---|
| YYYY-MM-DDThh:mm:ssZ |