利用権・会員資格
施設やサービスを利用する権利と、参加条件を管理する。
← 実装パターン一覧施設の利用パスを発行し、受付で資格を確認する
会員に施設の利用パスを1枚発行し、受付端末で本人のウォレットと保有状況を確認する構成です。PrivilegeEditionでは1つのIDが同じ条件のパスを表すため、同じ利用期間の会員をまとめて管理できます。
会員登録、資格の発行、受付での利用可否判定、利用履歴までをつなげられます。月間パスと1回券では、利用時の処理を変えて構成します。
システム構成と役割
会員画面
ログインとウォレットの所有確認を行います。
Authentication / Wallet linking資格・施設の管理
利用施設、期間、停止状態、パスIDとの対応を保存します。
Application DBパスの発行・消費
IDごとの保有数、発行数、譲渡条件を扱います。
PrivilegeEditionHelper / ERC-1155受付・利用履歴
資格と施設ルールを確認して入場を許可し、受付IDで記録します。
Reception API / Visit records
データモデル例
業務DBに保存する情報と、チェーンやAPIで管理する情報を対応づけます。フィールド名とサンプル値は、この構成で使うアプリケーション側の設計例です。
| 項目・値の例 | 管理する場所 | 役割・対応関係 |
|---|---|---|
memberId / walletAddressMEM-001 / 0x… | 業務DB | 署名やウォレットリンクで確認済みのアドレスを会員IDに紐づけます。 |
facilityId / editionIdLAB-TOKYO / 20270901 | 業務DB | 利用対象の施設と、共通条件のパスIDを対応づけます。 |
chainId / editionAddresschainId / 0x… | DBとチェーン | パスを発行するPrivilegeEditionを特定します。 |
expiresAt / tierUNIX seconds / 0 | チェーン+発行設定DB | この例のtierは0です。投票の重みを表す値で、入場ランクではありません。期限などはIDごとに共通です。 |
balance1 | チェーン | ERC-1155のbalanceOf(member, editionId)で取得します。 |
visitId / statusVISIT-001 / admitted | 業務DB | 受付の重複、同時利用、1回券の消費結果を管理します。 |
処理の流れ
パスの種類を設定する
施設・期間・譲渡可否を決め、IDを割り当てます。同じIDでは最初の発行で設定されたURI・期限・tierを共有するため、期間が異なるパスには別のIDを用意します。
PrivilegeEditionHelper.attach / soulbound本人のウォレットへ発行する
会員登録と所有確認を済ませ、会員へ1枚をmintします。氏名や連絡先は業務DBで管理し、公開URIには施設名や利用条件など公開可能な情報を掲載します。
mint(member, editionId, 1n, uri, 0, expiresAt, 0n)受付で利用可否を確認する
短い有効期限を持つチャレンジなどで本人を確認し、保有数・サーバー時刻による有効期間・施設の対象範囲・会員の停止状態を照合します。アドレスの入力や残高だけでは入場を許可しません。
Wallet ownership check → contract.balanceOf → application access rulesパスの種類に応じて利用を記録する
月間パスは残高を減らさずvisitIdで利用記録を保存します。1回券は保有者または承認済みoperatorの署名でredeemし、確定を確認してから入場を記録します。
Monthly: visit record / Single-use: redeem(holder, editionId, 1n)更新・失効を反映する
更新時は新しい期間のIDを発行します。期限切れ残高が残っていても受付では拒否し、必要に応じてsweepExpiredによる消込を行います。業務上の会員停止も受付ルールに反映します。
New editionId / sweepExpired / application suspension state
コードを利用する前の準備
- PrivilegeEditionの初期化と、発行者へのMINTER_ROLE付与を済ませます。譲渡不可の会員パスではsoulboundの設定を確認します。
- editionId・URI・期限を発行設定として保存します。同じIDへ異なる期限を渡して更新する使い方はしません。
- expiresAtはUNIX秒を渡します。利用時の判定は、信頼できる会員・施設の設定とサーバー時刻で行います。
npm install --save-exact @hazbase/kit@0.9.0 ethers@6.16.0以下はアプリケーションから呼び出すTypeScriptの関数です。接続先・権限・対象データを引数として渡します。UI、DBへの保存、処理の再開は、上記の構成に沿って業務側へ組み込めます。
利用パスを1枚発行する/1回券を消費する
issueFacilityPassは発行者のSignerを使用します。consumeFacilityPassは1回券用で、保有者自身のSignerを使用します。月間パスの入場処理からは消費用関数を呼び出しません。
import { PrivilegeEditionHelper } from '@hazbase/kit';
type Signer = Parameters<typeof PrivilegeEditionHelper.deploy>[1];
export async function issueFacilityPass(input: {
editionAddress: string;
member: string;
editionId: bigint;
metadataUri: string;
expiresAt: bigint;
issuer: Signer;
}) {
// One edition ID shares one URI, expiry and tier across all holders.
const pass = PrivilegeEditionHelper.attach(input.editionAddress, input.issuer);
const receipt = await pass.mint(
input.member, input.editionId, 1n,
input.metadataUri, 0, input.expiresAt, 0n,
);
// Standard ERC-1155 balanceOf is available on the underlying contract.
const balance = BigInt(await pass.contract.balanceOf(input.member, input.editionId));
return { balance: balance.toString(), transactionHash: receipt.hash };
}
export async function consumeFacilityPass(
editionAddress: string,
editionId: bigint,
holder: Signer,
) {
// Use for a single-use pass, after the service checks its validity.
const pass = PrivilegeEditionHelper.attach(editionAddress, holder);
const receipt = await pass.redeem(await holder.getAddress(), editionId, 1n);
return { transactionHash: receipt.hash, blockNumber: receipt.blockNumber };
}
実行結果と、アプリケーションへの反映
新規会員への発行後、対象IDの保有数は1になります。1回券のredeem後は残高が減り、RewardRedeemedイベントが記録されます。実際の入場やサービス提供は受付システム側で処理します。
実装で押さえておきたい点
残高と有効性は別に確認する
期限切れのパスも消込前は残高が残ることがあります。残高が1以上という条件に加えて、発行設定の期限・停止状態・施設を必ず照合します。
受付の二重処理
visitIdを一意にし、1回券は受付中・消費送信済み・確定・入場済みを分けます。通信が途切れた場合は同じ受付IDの状態を確認します。
個人ごとの有効期限
同じeditionIdの期限は共通です。入会日から30日など会員ごとに異なる条件では、期限別IDや別の資格モデルを選択します。
用途に合わせた拡張
保有者の署名なしで受付を進める場合
承認済みoperatorによる消費など、対象範囲を限定した操作権限を用意できます。QRコードの読取だけで利用者の資産操作権限を得られるわけではありません。
条件付き配布
Whitelistで許可対象を管理し、Voucherを使う場合は発行者、受取先、期限、再利用防止のnonceを含む署名条件と配布フローを組み合わせます。