# [EIP7291] 取引の条件が満たされた時に自動的に支払いを行うPBM（Purpose Bound Money）の仕組みを理解しよう！

> この提案は、PBM（Purpose Bound Money）というコンセプトを実装するために、ERC1155標準を拡張したスマートコントラクトインターフェースを構築するものです。

- URL: https://cardene.dev/articles/887a4ba382543d1666a1
- 公開日: 2024-04-19
- 更新日: 2024-04-19
- トピック: Blockchain
- タグ: ethereum, solidity, smartcontract, ブロックチェーン

## はじめに

初めまして。
**CryptoGames**というブロックチェーンゲーム企業でエンジニアをしている **cardene（かるでね）** です！
スマートコントラクトを書いたり、フロントエンド・バックエンド・インフラと幅広く触れています。

https://cryptogames.co.jp/

代表的なゲームは**クリプトスペルズ**というブロックチェーンゲームです。

https://cryptospells.jp/

今回は、**ERC20**互換のデジタルマネートークンと**ERC1155**互換のスマートコントラクトを使用して、取引の条件が満たされた時に自動的に支払いを行う、PBM（Purpose Bound Money）という仕組みであるを提案している**EIP7291**についてまとめていきます！

:::message
以下にまとめられているものを翻訳・要約・補足しながらまとめていきます。
:::

https://eips.ethereum.org/EIPS/eip-7291

他にも様々なEIPについてまとめています。

https://cardene.notion.site/ERC-EIP-2a03fa3ea33d43baa9ed82288f98d4a9?pvs=4

## 概要

この提案は、PBM（Purpose Bound Money）というコンセプトを実装するために、**ERC1155**標準を拡張したスマートコントラクトインターフェースを構築するものです。

:::message
**ERC1155**については以下の記事を参考にしてください。
:::

https://chaldene.net/erc1155

PBMは「**PBMラッパー**」とそれがラップする「**デジタルマネートークン**」から構成されています。
このデジタルマネートークンにはステーブルコイン、中央銀行デジタル通貨、トークン化された**vault**などが含まれ、価値の保存手段（**sov**）として機能します。
このため、デジタルマネートークン（**sovToken**）は以下の特性を持つべきです。

- 良好な価値の保存手段。
- 適切な計算単位。
- 交換媒体。

:::message
トークンのラップ（Wrapping）とは、ある種のデジタルアセットやトークンを別の形式に「**包む**」ことで、そのアセットを新しい環境やプラットフォームで利用可能にするプロセスです。
具体的には、元々のトークンの機能性や価値を保持しつつ、異なるブロックチェーンやスマートコントラクトシステム上で動作可能な新しいトークンとして再発行することを指します。

例えば、ビットコイン（BTC）はそのままではイーサリアムブロックチェーン上で直接使用することはできません。
しかし、BTCをイーサリアムのスマートコントラクトで使用できる**ERC20**トークン形式に「**ラップ**」することで、イーサリアムのデジタルエコシステムで活用することが可能になります。
このラップされたビットコインは通常「**Wrapped Bitcoin（WBTC）**」と呼ばれ、イーサリアムネットワーク上で様々なデジタルサービスやアプリケーションで使用できるようになります。

このプロセスは主に以下のステップで行われます。
- **デポジット**
    - ユーザーは元のトークン（例：BTC）をラッパーと呼ばれる特定のアドレスに送ります。
- **発行**
    - ラッパーはユーザーがデポジットしたトークンに相当する新しいトークン（例：WBTC）を発行します。
    - この新しいトークンは元のトークンの価値を代表し、新しいプラットフォームで利用可能です。
- **交換と使用**
    - ラップされたトークンは新しいブロックチェーン環境内で自由に取引や利用が可能です。

ラッププロセスには通常、信頼できるカストディアンやスマートコントラクトが関与しており、デポジットされたオリジナルのトークンの安全を確保し、ラップされたトークンの発行と管理を行います。
この方法で、ブロックチェーン間の相互運用性が向上し、さまざまなアセットがより広い範囲で活用されるようになります。
:::

PBMは、内蔵されたプログラミングロジックを持つ無記名証券として設計されており、仲介者を介さずに二者間での移転が可能です。
このシステムは以下の二つの概念を組み合わせています。
- プログラム可能な支払い
    - 事前に定義された条件が満たされた時に、自動的に支払いを実行します。
- プログラム可能なマネー
    - 交換手段自体にルールを埋め込むことで、その使用方法を定義または制約する可能性があります。

条件が満たされると、sovTokenは**unwrapping**され、再び自由な状態に戻ります。
PBMはデジタルキャッシュバウチャーとして考えることができ、支払い者の使用方法に制約を設ける一方で、事業者などには制約を課しません。
PBMは支払い時にステーブルコインを**unwrapping**します。
これは、発行者によって指定された事業者からの購入に限定される物理的なキャッシュバウチャーに似ており、バウチャーを受け取る事業者はそれを発行者と交換して法定通貨を受け取ることができます。

:::message

#### キャッシュバウチャー

キャッシュバウチャーは、特定の金額が事前に設定された支払い手段であり、主に贈り物として利用されることが多いですが、プロモーションやリワードプログラムの一環としても使用されます。
これは、事前に支払われた金額に相当する購買力を持ち、指定された店舗やサービスプロバイダーでのみ使用可能なクーポンやカードの形をとることが多いです。

#### キャッシュバウチャーの特徴と利用例

- **事前支払い**
    - ユーザーはバウチャーを購入する時に、そのバウチャーの額面価値分を前払いします。
    - 例えば、1000円のキャッシュバウチャーを購入した場合、そのバウチャーは1000円分の商品やサービスと交換できます。

- **使用制限**
    - バウチャーは、特定の店舗、レストラン、オンラインプラットフォームなど、使用できる場所が限定されています。
    - また、使用できる期限が設けられていることもあります。

- **非現金化**
    - キャッシュバウチャーは現金として引き出すことはできません。
    - バウチャーの金額は、商品やサービスの購入にのみ使用されます。

- **贈り物や報酬としての利用**
    - よく贈り物として使われ、受け取った人は自由にそのバウチャーで商品を選び購入することができます。
    - また、企業が従業員へのインセンティブや顧客へのリワードとして提供することもあります。

##### 利点と制限
- **利点**
    - 使用者はバウチャーを使って、金銭的な支払いを直接行うことなく商品やサービスを受けることができ、贈る側は受け取る人が好きな商品を自由に選べるという柔軟性を提供できます。
- **制限**
    - 使用できる場所や期限が限られており、一部のユーザーにとってはそれが不便に感じられることもあります。

#### デジタルキャッシュバウチャー

デジタルキャッシュバウチャーは、物理的なキャッシュバウチャーやギフトカードに似たデジタル形式の支払い手段です。
通常、特定の金額がプリロードされており、特定の販売者やサービスプロバイダーでのみ使用できるという制約があります。
デジタルキャッシュバウチャーは、消費者がオンラインまたは実店舗で商品やサービスを購入する際に利用することができます。

デジタルキャッシュバウチャーの特徴は次のとおりです。
- **事前に支払われた金額**
    - ユーザーはバウチャーを購入する際にあらかじめ決められた金額を支払います。
- **使用制限**
    - バウチャーは特定の店舗やサービスでのみ使用できることが一般的です。
- **デジタル形式**
    - 物理的なカードや紙の代わりに、電子コードやデジタルトークンとして管理されます。

PBM（Purpose Bound Money）の文脈では、デジタルキャッシュバウチャーとして機能し、特定の使い道に制限されたデジタル通貨としての性質を持ちます。
消費者は特定の条件下でのみPBMを使用でき、事業者などはそれを受け取り、ベースとなる通貨（たとえば法定通貨やステーブルコイン）に換金することができます。
これにより、PBMは特定の取引のために使用されるよう設計されていますが、一旦支払いが完了すれば、事業者に対してはそのような制約は適用されません。
:::

この提案では、以下のモジュラー構造を提案しています。
- デジタルマネーとしての**ERC20**互換の**sovToken**。
- PBMラッパーとしての**ERC1155**互換のスマートコントラクト。
- PBMロジックとしてのアドレスリストスマートコントラクト。
- トークンの登録と取り出しを管理するPBMトークンマネージャースマートコントラクト。

これらの要素が組み合わさることで、PBMの効率的かつ柔軟な管理と運用が可能になり、開発者や参加者が独自の機能やルールを容易に組み込めるようになります。

:::message
**ERC20**については以下の記事を参考にしてください。
:::

https://chaldene.net/erc20

## 動機

この提案の動機は、技術の断片化を防ぎ、それによって生じる相互運用性の欠如を防止することにあります。
PBM（Purpose Bound Money）の仕様をオープンにすることで、新しい参加者は既存の市場標準へ簡単かつ無料でアクセスできるようになります。
これにより、異なるプラットフォーム、ウォレット、支払いシステム、レール間での相互運用性が可能になります。
その結果、新規参入者の参入コストが低下し、活発な支払い環境が育まれ、閉鎖的な環境や独占の発展が防がれます。
これにより、最終的にはより効率的で手頃なサービスと、より良いユーザーエクスペリエンスが実現されると考えられています。

:::message
この提案は業界全体で共通の基準を設けることによって、新しい市場参入者が技術的な障壁に直面することなく、既存のエコシステムに簡単に組み込めるようにすることを目的としています。
相互運用性の向上は、さまざまなデジタル決済ソリューションがスムーズに連携することを可能にし、全体としてのデジタル決済の効率と利便性を高めます。
これは長期的にはユーザーにとってより良い選択肢と経験をもたらすことになります。
:::

## 仕様

### 定義

#### sovToken
**sovToken**は、**ERC20**または**ERC20**互換のデジタル通貨（例えば**ERC777**、**ERC1363**など）で、価値の保存手段（つまりPBMトークンの裏付けとなる担保）として機能します。
このトークンは、取引の安全性と価値の安定を提供する基礎となるアセットです。

:::message
**ERC777**については以下の記事を参考にしてください。
:::

[\[ERC777\] トークンコントラクトの標準インターフェースの仕組みを理解しよう！](/articles/c0fcb8f3122f66985226)

:::message
**ERC1363**については以下の記事を参考にしてください。
:::

[\[ERC1363\] ERC20のtransferやapprove後に追加で関数を実行する仕組みを理解しよう！](/articles/18e3a3e8104951b2d057)


#### PBM Wrapper
**PBM Wrapper**は**ERC1155**に準拠したスマートコントラクトで、1つまたは複数の条件が満たされる必要があることを指定して**sovToken**をラップします。
これらの条件はPBMビジネスロジックと呼ばれ、PBMアーキテクチャのセクションで詳細が説明されています。
PBMラッパーはモジュラー設計が可能で、コア、プラグイン、フックスのコンポーネントを含むことができます。
PBMラッパースマートコントラクトは、採用されたフックススマートコントラクトと共に、条件が満たされる前に**sovToken**のラッピングを解除します。

#### PBM Token
**PBM Token**は、**sovToken**とそのPBMラッパーの組み合わせを指し、**ERC1155**トークンとして表されます。
PBMトークンは、特定の条件を満たすことで初めて利用可能になる設計がされています。

#### PBM Creator
**PBM Creator**は、PBMラッパーの条件を定義し、PBMトークンを作成する役割を担います。
このエンティティはトークンの使用条件や目的を設定し、どのような場合にトークンが利用できるかを制御します。

#### PBM Wallet
**PBM Wallet**は、プライベートキーによって制御されるEOA（Externally Owned Account）またはスマートコントラクトウォレットのいずれかであり、PBMトークンを保管、管理するための暗号化ウォレットです。

#### Merchant / Redeemer
**Merchant / Redeemer**は、PBMトークンが最終的に向けられるか、目的限定である受取人またはエンドポイントとして広く定義されています。
事業者のアイデンティティは、PBMラッパースマートコントラクトの一部であるか、PBMフックとして登録された独立したPBMアドレスリストスマートコントラクトで指定されます。

### 概要

#### PBMのWrappingとUnwrapping
- **Wrapping**
    - トークンを「**Wrap**」することは、PBMビジネスロジックに基づいて特定の条件に従ってそのトークンを制限することを意味します。
    - これはトークンが特定の使命や条件に従ってのみ使用されるようにするためのものです。
- **Unwrapping**
    - トークンの「**Unwrapping**」は、PBMビジネスロジックの条件が満たされた時に、制約が解除され、トークンが自由になるプロセスを指します。
    - これにより、トークンは通常の通貨として使用可能になります。

#### PBMトークンの構造
- **有効なPBMトークン**
    - 有効なPBMトークンは、基本となるsovToken（**ERC20**互換トークン）とPBMラッパーから構成されます。
- **ラッピングのタイミング**
    - **sovToken**は、PBMトークンの作成時またはそれ以降の任意の時点でラップ可能です。
- **トランザクションの条件の確認**
    - PBMラッパーは、すべての取引当事者がトークンを**unwrapping**する前に必要な条件が満たされたことを確認できるメカニズムを提供しなければなりません。
    - これはPBMラッパー内または別のPBMフックスマートコントラクトで実装される可能性があります。
- **ホワイトリストとブラックリストの管理**
    - **unwrapping**されるsovTokenの宛先アドレスは、**unwrapping**前にMerchant/Redeemerアドレスのホワイトリストに含まれ、禁止アドレスのブラックリストには含まれないことが必要です。
- **トークンの破棄**
    - PBMトークンは、完全に**unwrapping**され使用された後、`burn`されなければなりません。

#### その他の要件
- **有効期限**
    - PBMトークンは、PBMクリエーターによって設定された有効期限を持つべきです。
    - 有効期限が不要な場合、その期限は無限に設定されるべきです。

#### 仕様の拡張
この提案はPBMの基本仕様を定義しており、この基本仕様に基づいて別の仕様が実装されることが可能です。
これにより、PBMの機能や応用範囲を拡大するための追加的な拡張が可能になります。

この概要は、PBMがどのように機能するか、そしてどのようにしてビジネスロジックに基づいた運用が行われるかについての基本的なフレームワークを提供します。
これにより、PBMトークンは特定の目的に限定された利用が可能となり、安全かつ効率的なデジタルトランザクションを実現するための強力なツールとなることが期待されます。

### PBMアーキテクチャ

このEIP（Ethereum Improvement Proposal）では、モジュラー設計のPBM（Purpose Bound Money）アーキテクチャを提案しています。このアーキテクチャは、**コア部分**、**プラグイン部分**、そして**フック部分**という三つの異なるコンポーネントから構成されています。それぞれのコンポーネントの役割と機能を詳しく見ていきましょう。

#### 1. コアコンポーネント
コア部分には、すべてのPBMが持つべき基本機能と検証チェックが含まれています。これには以下が含まれます：
- **sovTokenとPBMラッパー**：基本ロジック（例えば、商人/償還者のアドレスをホワイトリストに登録するロジック、ホワイトリストに登録されたアドレスへの転送時に**unwrapping**するロジック、PBMの発行と焼却のロジックなど）を含む。
- **トークンマネージャー**：トークンの登録や取り出しを可能にします。

また、PBMラッパーは、プラグインやフックとインターフェイスするロジックを含むこともあります。

#### 2. プラグインコンポーネント
プラグイン部分は、特定のPBMが必要とする追加機能を実装します。これには以下のようなロジックが含まれるかもしれません：
- **外部APIの呼び出し**：特定のPBM条件が満たされたかを確認するため。
- **PBM使用パターンの追跡**：使用されるパターンや状況を把握するため。

#### 3. フックコンポーネント
フック部分は、一部のPBMが必要とする追加の検証チェックを実装します。これには以下が含まれます：
- **有効期限のチェック**
- **日次の支出制限**
- **商品・サービスの制限**

#### 使用例
たとえば、PBMの作成者が、PBMシリーズAの50%までのみスーパーマーケットで使えるようにしたい場合（PBMシリーズBには制限なし）、スーパーマーケットでの支出を追跡するプラグインスマートコントラクトを実装することができます。さらに、ユーザーが発行されたPBMシリーズAの50%未満をスーパーマーケットで使用していることを検証するフックも実装します。これにより、同じ汎用のPBMラッパーとsovTokenをPBMシリーズAとBの両方に使用することが可能になります。また、PBMシリーズAは、追加のデータ追跡と検証のためにプラグインモジュールとフックモジュールを登録します。

このように、PBMアーキテクチャは非常に柔軟で、さまざまなビジネスニーズや規制要件に対応するためにカスタマイズ可能な構造を提供します。これにより、さまざまなシナリオや市場条件に適応しながら、トランザクションの透明性と効率性を高めることができます。

### 監査可能性

PBMラッパーは、トランザクションの透明性と検証可能性を高めるために、スマートコントラクトのロジックを簡単にアクセス可能で確認可能な方法で提供するべきです。
これにより、自動検証や非同期ユーザー検証が行われ、取引当事者やホワイトリストに登録された第三者が証明を提供する際に利用できます。
これは、以下のような方法で実現されます。

- **インターフェース/イベントの発行**
    - これにより、トランザクションの履歴、トークンの種類、トークンの残高を詳細に確認できるようになります。
    - これは監査時に非常に有用で、どのような操作が行われたかを正確に追跡することが可能です。
- **ソースコードの検証と公開**
    - スマートコントラクトのソースコードはブロックチェーンエクスプローラーに検証済みで公式に公開されるべきです。
    - これにより、コードが変更されていないことや、実際にデプロイされているロジックが公開されていることが保証されます。

### 代替性
PBMラッパーは、異なる種類の互換性のある**sovToken**（例：**USDC**や**XSGD**）をラップすることができるべきです。
同一のPBMラッパーによってラップされる**sovToken**は、互いに代替可能である可能性があります。
この標準は、実装がどのように行われるべきかについて具体的な指示を出していません。
つまり、開発者には柔軟性が与えられており、異なるsovTokenのラップ方法や代替可能性を自由に設計することができます。
これは、PBMの使用範囲を広げ、様々な市場やシナリオに適応する能力を高めるため重要です。

### PBMトークン詳細

このセクションでは、ERC-1155マルチトークンスタンダードを基にしたPBM（Purpose Bound Money）トークンの詳細な構造について説明します。ERC-1155は、一つのスマートコントラクト内で複数のトークンタイプを管理することができるフレキシブルなトークン規格です。この規格を利用して、各PBMトークンのIDがユニークな設定可能なトークンタイプに対応するように設計されています。

#### PBMトークンの構造体（PBMToken）
`PBMToken` 構造体は、PBMトークンのIDごとに必要な詳細情報を保持します。

:::details コード

```ts
pragma solidity ^0.8.0;

abstract contract IPBMRC1_TokenManager {
    /// @dev Mapping of each ERC-1155 tokenId to its corresponding PBM Token details.
    mapping (uint256 => PBMToken) internal tokenTypes ;

    /// @notice A PBM token MUST include compulsory state variables (name, faceValue, expiry, and uri) to adhere to this standard.
    /// @dev Represents all the details corresponding to a PBM tokenId.
    struct PBMToken {
        // Name of the token.
        string name;

        // Value of the underlying wrapped ERC20-compatible sovToken. Additional information on the `faceValue` can be specified by
        // adding the optional variables: `currencySymbol` or `tokenSymbol` as indicated below
        uint256 faceValue;

        // Time after which the token will be rendered useless (expressed in Unix Epoch time).
        uint256 expiry;

        // Metadata URI for ERC-1155 display purposes.
        string uri;

        // OPTIONAL: Indicates if the PBM token can be transferred to a non merchant/redeemer wallet.
        bool isTransferable;

        // OPTIONAL: Determines whether the PBM will be burned or revoked upon expiry, under certain predefined conditions, or at the owner's discretion.
        bool burnable;

        // OPTIONAL: Number of decimal places for the token.
        uint8 decimals;

        // OPTIONAL: The address of the creator of this PBM type on this smart contract. This field is optional because the creator is msg.sender by default.
        address creator;

        // OPTIONAL: The smart contract address of the sovToken.
        address tokenAddress;

        // OPTIONAL: The running balance of the PBM Token type that has been minted.
        uint256 totalSupply;

        // OPTIONAL: An ISO4217 three-character alphabetic code may be needed for the faceValue in multicurrency PBM use cases.
        string currencySymbol;

        // OPTIONAL: An abbreviation for the PBM token name may be assigned.
        string tokenSymbol;

        // Add other optional state variables below...
    }
}
```

:::


- **name**
    - トークンの名前。
- **faceValue**
    - ラップされた**ERC20**互換の**sovToken**の価値。
    - この値には追加情報を指定するためのオプショナルな変数（`currencySymbol` や `tokenSymbol` など）を追加することができます。
- **expiry**
    - トークンが無効となる時刻（Unix Epoch時刻で表現）。
- **uri**
    - **ERC1155**表示目的のメタデータURI。
- **isTransferable** (オプショナル)
    - PBMトークンが事業者以外のウォレットに`transfer`可能かどうか。
- **burnable** (オプショナル)
    - トークンが有効期限時、あるいは特定の事前定義された条件下、または所有者により`burn`されるかどうか。
- **decimals** (オプショナル)
    - トークンの小数点以下の桁数。
- **creator** (オプショナル)
    - このPBMタイプの作成者のアドレス。
    - デフォルトでは `msg.sender`（メッセージ送信者）が作成者とされます。
- **tokenAddress** (オプショナル)
    - **sovToken**のスマートコントラクトアドレス。
- **totalSupply** (オプショナル)
    - このPBMトークンタイプの発行総量。
- **currencySymbol** (オプショナル)
    - 複数通貨のPBMの使用例で必要とされる場合、額面価値に関連するISO4217の3文字の通貨コード。
- **tokenSymbol** (オプショナル)
    - PBMトークン名の略称。

#### トークンの不変性
一度定義されたトークンの詳細は不変でなければなりません。
これは、トークンの信頼性と監査の正確性を保証するために重要です。
トークンの特性が変更されることがないようにすることで、利用者や監査者がトークンが持つ情報の一貫性と正確性を信頼することができます。

#### 将来の提案と拡張性
将来的には、追加のオプショナルな状態変数が必要に応じて定義されるかもしれません。
これにより、PBMトークンはより多様なニーズに応じてカスタマイズ可能となり、異なる市場や用途での使用が想定されます。

PBM（Purpose Bound Money）トークンタイプを作成し、管理するための機能が定義されています。
これにより、PBMトークンの柔軟な管理と拡張が可能になります。

#### 新しいPBMトークンタイプの作成
`createPBMTokenType` 関数は、新しいPBMトークンタイプを作成するために使用されます。
この関数は以下のパラメータを受け取ります。

- **_name**
    - トークンの名前。
- **_faceValue**
    - ラップされる**ERC20**互換の**sovToken**の価値。
- **_tokenExpiry**
    - トークンが無効となる時刻（Unix Epoch時刻で表現）。
- **_tokenURI**
    - **ERC1155**表示目的のメタデータURI。

この関数は新しいトークンタイプを作成し、そのトークンIDを返します。
また、新しいトークンタイプが作成されるたびに`NewPBMTypeCreated`イベントを発行します。

```ts
function createPBMTokenType(
    string memory _name,
    uint256 _faceValue,
    uint256 _tokenExpiry,
    string memory _tokenURI
) external virtual returns (uint256 tokenId_);
```

#### `NewPBMTypeCreated` イベント
新しいPBMトークンタイプが作成されたことを通知するイベントです。

- **tokenId**
    - 新しく作成されたPBMトークンタイプの一意の識別子。
- **tokenName**
    - 新しく作成されたPBMトークンタイプの名前。
- **amount**
    - 新しく作成されたPBMトークンタイプの初期供給量。
- **expiry**
    - 新しく作成されたPBMトークンタイプの有効期限。
- **creator**
    - 新しいPBMトークンタイプを作成したアカウントのアドレス。

```ts
event NewPBMTypeCreated(uint256 tokenId, string tokenName, uint256 amount, uint256 expiry, address creator);
```

#### PBMトークンタイプの詳細取得
`getTokenDetails` 関数は、指定されたtokenIdに関連付けられたPBMトークンの詳細を取得するために使用されます。

```ts
function getTokenDetails(uint256 tokenId) external virtual view returns(PBMToken memory pbmToken_);
```

### PBMアドレスリスト

:::details コード

```ts
pragma solidity ^0.8.0;

/// @title PBM Address list Interface.
/// @notice The PBM address list stores and manages whitelisted merchants/redeemers and blacklisted address for the PBMs
interface IPBMAddressList {

    /// @notice Checks if the address is one of the blacklisted addresses
    /// @param _address The address to query
    /// @return bool_ True if address is blacklisted, else false
    function isBlacklisted(address _address) external returns (bool bool_) ;

    /// @notice Checks if the address is one of the whitelisted merchant/redeemer addresses
    /// @param _address The address to query
    /// @return bool_ True if the address is in merchant/redeemer whitelist and is NOT a blacklisted address, otherwise false.
    function isMerchant(address _address) external returns (bool bool_) ;

    /// @notice Event emitted when the Merchant/Redeemer List is edited
    /// @param action Tags "add" or "remove" for action type
    /// @param addresses An array of merchant wallet addresses that was just added or removed from Merchant/Redeemer whitelist
    /// @param metadata Optional comments or notes about the added or removed addresses.
    event MerchantList(string action, address[] addresses, string metadata);

    /// @notice Event emitted when the Blacklist is edited
    /// @param action Tags "add" or "remove" for action type
    /// @param addresses An array of wallet addresses that was just added or removed from address blacklist
    /// @param metadata Optional comments or notes about the added or removed addresses.
    event Blacklist(string action, address[] addresses, string metadata);
}
```

:::


### PBMRC1ベースインターフェース

:::details コード

```ts
pragma solidity ^0.8.0;

/// LIST OF EVENTS TO BE EMITTED
/// A database or explorer may listen to events and be able to provide indexed and categorized searches
/// @title PBM Specification interface
/// @notice The PBM (purpose bound money) allows us to add logical requirements on the use of sovTokens.
/// The PBM acts as wrapper around the sovTokens and implements the necessary business logic.
/// @dev PBM deployer must assign an overall owner to the smart contract. If fine grain access controls are required, EIP-5982 can be used on top of ERC173
interface IPBMRC1 is IERC173, IERC5679Ext1155 {

    /// @notice Initialise the contract by specifying an underlying ERC20-compatible token address,
    /// contract expiry, and the PBM address list.
    /// @param _sovToken The address of the underlying sovToken.
    /// @param _expiry The contract-wide expiry timestamp (in Unix epoch time).
    /// @param _pbmWrapperLogic This address should point to a smart contract that contains conditions governing a PBM;
    /// such as purpose-bound conditions (ie: an address list determining whether a PBM is permitted to be transferred to or to be unwrapped)
    /// and other relevant business logic, effectively implementing an inversion of control.
    function initialise(address _sovToken, uint256 _expiry, address _pbmWrapperLogic) external;

    /// @notice Returns the Uniform Resource Identifier (URI) metadata information for the PBM with the corresponding tokenId
    /// @dev URIs are defined in RFC 3986.
    /// The URI MUST point to a JSON file that conforms to the "ERC-1155 Metadata URI JSON Schema".
    /// Developer may choose to adhere to the ERC1155Metadata_URI extension interface if necessary.
    /// The URI is not expected to be immutable.
    /// @param tokenId The id for the PBM in query
    /// @return Returns the metadata URI string for the PBM
    function uri(uint256 tokenId) external  view returns (string memory);

    /**
        @notice Creates a PBM copy ( ERC1155 NFT ) of an existing PBM token type.
        @dev See {IERC5679Ext1155} on further implementation notes
        @param receiver The wallet address to which the created PBMs need to be transferred to
        @param tokenId The identifier of the PBM token type to be copied.
        @param amount The number of the PBMs that are to be created
        @param data Additional data with no specified format, based on eip-5750

        This function will transfer the underlying token from the caller into the PBM smart contract.
        IMPT: Before minting, the caller should approve the contract address to spend sovTokens on behalf of the caller.
            This can be done by calling the `approve` or `increaseMinterAllowance` functions of the ERC-20 contract and specifying `_spender` to be the PBM contract address.
            Ref : https://eips.ethereum.org/EIPS/eip-20

        WARNING: Any contracts that externally call these safeMint() and safeMintBatch() functions should implement some sort of reentrancy guard procedure
        (such as OpenZeppelin's ReentrancyGuard) or a Checks-effects-interactions pattern.

        As per ERC-5679 standard: When the token is being minted, the transfer events MUST be emitted as if the token in the `amount` for EIP-1155
        and `tokenId` being _id for EIP-1155 were transferred from address 0x0 to the recipient address identified by receiver.
        The total supply MUST increase accordingly.

        MUST Emits {TokenWrap} event as the underlying sovToken is wrapped by PBM wrapper smart contract during minting.

        Requirements:
        - contract must not be paused
        - tokens must not be expired
        - `tokenId` should be a valid id that has already been created
        - caller should have the necessary amount of the sovTokens required to mint
        - caller should have approved the PBM contract to spend the sovTokens
        - receiver should not be blacklisted
     */
    function safeMint(address receiver, uint256 tokenId, uint256 amount, bytes calldata data) external;

    /**
        @notice Creates multiple PBM copies (ERC1155 NFT) of an existing PBM token type.
        @dev See {IERC5679Ext1155}.
        @param receiver The wallet address to which the created PBMs need to be transferred to
        @param tokenIds The identifier of the PBM token type
        @param amounts The number of the PBMs that are to be created
        @param data Additional data with no specified format, based on eip-5750

        This function will transfer the underlying token from the caller into the PBM smart contract.
        IMPT: Before minting, the caller should approve the contract address to spend sovTokens on behalf of the caller.
            This can be done by calling the `approve` or `increaseMinterAllowance` functions of the ERC-20 contract and specifying `_spender` to be the PBM contract address.
            Ref : https://eips.ethereum.org/EIPS/eip-20

        WARNING: Any contracts that externally call these safeMint() and safeMintBatch() functions should implement some sort of reentrancy guard procedure
        (such as OpenZeppelin's ReentrancyGuard) or a Checks-effects-interactions pattern.

        As per ERC-5679 standard: When the token is being minted, the transfer events MUST be emitted as if the token in the `amount` for EIP-1155
        and `tokenId` being _id for EIP-1155 were transferred from address 0x0 to the recipient address identified by receiver.
        The total supply MUST increase accordingly.

        MUST Emits {TokenWrap} event as the underlying sovToken is wrapped by PBM wrapper smart contract during minting.

        Requirements:
        - contract must not be paused
        - tokens must not be expired
        - `tokenIds` should all be valid ids that have already been created
        - `tokenIds` and `amounts` list need to have the same number of values
        - caller should have the necessary amount of the sovTokens required to mint
        - caller should have approved the PBM contract to spend the sovTokens
        - receiver should not be blacklisted
     */
    function safeMintBatch(address receiver, uint256[] calldata tokenIds, uint256[] calldata amounts, bytes calldata data) external;

    /**
        @notice Burns a PBM token. Upon burning of the tokens, the underlying wrapped token (if any) should be handled.
        @dev Destroys `amount` tokens of token type `tokenId` from `from`
        @dev See {IERC5679Ext1155}

        @param from The originating wallet address of the PBMs to be burned
        @param tokenId The identifier of the PBM token type
        @param amount The amount of the PBMs that are to be burned
        @param data Additional data with no specified format, based on eip-5750

        MUST Emits {TransferSingle} event.
        MUST Emits {TokenUnwrapForPBMBurn} event if the underlying wrapped token is moved out of the PBM smart contract.

        Requirements:
        - `from` cannot be the zero address.
        - `from` must have at least `amount` tokens of token type `tokenId`.

     */
    function burn(address from, uint256 tokenId, uint256 amount, bytes calldata data) external;

    /**
        @notice Burns multiple PBM token. Upon burning of the tokens, the underlying wrapped token (if any) should be handled.
        @dev Destroys `amount` tokens of token type `tokenId` from `from`
        @dev See {IERC5679Ext1155}

        @param from The originating wallet address of the PBMs to be burned
        @param tokenIds The identifier of the PBM token types
        @param amounts The amount of the PBMs that are to be burned for each tokenId in _tokenIds
        @param data Additional data with no specified format, based on eip-5750

        Must Emits {TransferSingle} event.
        Must Emits {TokenUnwrapForPBMBurn} event if the underlying wrapped token is moved out of the PBM smart contract.

        Requirements:
        - `from` cannot be the zero address.
        - `from` must have at least amount specified in `_amounts` of the corresponding token type tokenId in `_tokenIds` array.
     */
    function burnBatch(address from, uint256[] calldata tokenIds, uint256[] calldata amounts, bytes calldata data) external;

    /// @notice Transfers the PBM(NFT) from one wallet to another.
    /// @dev This function extends the ERC-1155 standard in order to allow the PBM token to be freely transferred between wallet addresses due to
    /// widespread support accross wallet providers. Specific conditions and restrictions on whether a pbm can be moved across addresses can be incorporated in this function.
    /// Unwrap logic MAY also be placed within this function to be called.
    /// @param from The account from which the PBM (NFT) is moving from
    /// @param to The account which is receiving the PBM (NFT)
    /// @param id The identifier of the PBM token type
    /// @param amount The number of (quantity) the PBM type that are to be transferred of the PBM type
    /// @param data To record any data associated with the transaction, can be left blank if none
    function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes memory data) external;

    /// @notice Transfers the PBM(NFT)(s) from one wallet to another.
    /// @dev This function extends the ERC-1155 standard in order to allow the PBM token to be freely transferred between wallet addresses due to
    /// widespread support accross wallet providers.  Specific conditions and restrictions on whether a pbm can be moved across addresses can be incorporated in this function.
    /// Unwrap logic MAY also be placed within this function to be called.
    /// If the receiving wallet is a whitelisted /redeemer wallet address, the PBM(NFT)(s) will be burnt and the underlying sovTokens will be transferred to the merchant/redeemer wallet instead.
    /// @param from The account from which the PBM (NFT)(s) is moving from
    /// @param to The account which is receiving the PBM (NFT)(s)
    /// @param ids The identifiers of the different PBM token type
    /// @param amounts The number of (quantity) the different PBM types that are to be created
    /// @param data To record any data associated with the transaction, can be left blank if none.
    function safeBatchTransferFrom(address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data) external;

    /// @notice Unwraps the underlying ERC-20 compatible tokens to an intended end point (ie: merchant/redeemer) upon fulfilling the required PBM conditions.
    /// @dev Add implementation specific logic for the conditions under which a PBM processes and transfers the underlying tokens here.
    /// e.g. If the receving wallet is a whitelisted merchant/redeemer wallet address, the PBM (NFT) MUST be burnt and the underlying sovTokens
    /// will unwrapped to be transferred to the merchant/redeemer wallet.
    /// MUST emit the event {TokenUnwrapForTarget} on success
    /// @param from The account currently holding the PBM
    /// @param to The account receiving the PBM (NFT)
    /// @param tokenId The identifier of the PBM token type
    /// @param amount The quantity of the PBM type involved in this transaction
    /// @param data Additional data without a specified format, based on EIP-5750
    function unwrap(address from, address to, uint256 tokenId, uint256 amount, bytes memory data) internal;

    /// @notice Allows the creator of a PBM token type to retrieve all locked-up underlying sovTokens within that PBM.
    /// @dev Ensure that only the creator of the PBM token type or the contract owner can call this function.
    /// Validate the token state and existence, handle PBM token burning if necessary, safely transfer the remaining sovTokens to the originator,
    /// MUST emit {PBMrevokeWithdraw} upon a successful revoke.
    /// @param tokenId The identifier of the PBM token type
    /// Requirements:
    /// - `tokenId` should be a valid identifier for an existing PBM token type.
    /// - The caller must be either the creator of the token type or the smart contract owner.
    function revokePBM(uint256 tokenId) external;

    /// @notice Emitted when a PBM type creator withdraws the underlying sovTokens from all the remaining expired PBMs
    /// @param beneficiary the address ( PBM type creator ) which receives the sovToken
    /// @param PBMTokenId The identifiers of the different PBM token type
    /// @param sovToken The address of the underlying sovToken
    /// @param sovTokenValue The number of underlying sovTokens transferred
    event PBMrevokeWithdraw(address beneficiary, uint256 PBMTokenId, address sovToken, uint256 sovTokenValue);

    /// @notice Emitted when the underlying tokens are unwrapped and transferred to a specific purpose-bound address.
    /// This event signifies the end of the PBM lifecycle, as all necessary conditions have been met to release the underlying tokens to the recipient (whitelisted merchant/redeemer with non-blacklisted wallet address).
    /// If there are multiple different underlying tokens involved in a single unwrap operation, this event should be emitted for each underlying token.
    /// @param from The address from which the PBM tokens are being unwrapped.
    /// @param to The purpose-bound address receiving the unwrapped underlying tokens.
    /// @param tokenIds An array containing the identifiers of the unwrapped PBM token types.
    /// @param amounts An array containing the quantities of the corresponding unwrapped PBM tokens.
    /// @param sovToken The address of the underlying sovToken.
    /// @param sovTokenValue The amount of unwrapped underlying sovTokens transferred.
    event TokenUnwrapForTarget(address from, address to, uint256[] tokenIds, uint256[] amounts, address sovToken, uint256 sovTokenValue);

    /// @notice Emitted when PBM tokens are burned, resulting in the unwrapping of the underlying tokens for the designated recipient.
    /// This event is required if there is an unwrapping of the underlying tokens during the PBM (NFT) burning process.
    /// If there are multiple different underlying tokens involved in a single unwrap operation, this event should be emitted for each underlying token.
    /// @param from The address from which the PBM tokens are being burned.
    /// @param to The address receiving the unwrapped underlying tokens.
    /// @param tokenIds An array containing the identifiers of the burned PBM token types.
    /// @param amounts An array containing the quantities of the corresponding burned PBM tokens.
    /// @param sovToken The address of the underlying sovToken.
    /// @param sovTokenValue The amount of unwrapped underlying sovTokens transferred.
    event TokenUnwrapForPBMBurn(address from, address to, uint256[] tokenIds, uint256[] amounts, address sovToken, uint256 sovTokenValue);

    /// Indicates the wrapping of an token into the PBM smart contract.
    /// @notice Emitted when underlying tokens are wrapped within the PBM smart contract.
    /// If there are multiple different underlying tokens involved in a single wrap operation, this event should be emitted for each underlying token.
    /// This event signifies the beginning of the PBM lifecycle, as tokens are now managed by the conditions within the PBM contract.
    /// @param from The address initiating the token wrapping process, and
    /// @param tokenIds An array containing the identifiers of the token types being wrapped.
    /// @param amounts An array containing the quantities of the corresponding wrapped tokens.
    /// @param sovToken The address of the underlying sovToken.
    /// @param sovTokenValue The amount of wrapped underlying sovTokens transferred.
    event TokenWrap(address from, uint256[] tokenIds, uint256[] amounts,address sovToken, uint256 sovTokenValue);
}
```

:::

上記のSolidityコードは、PBM（Purpose Bound Money）トークンに関連するアドレスリストを管理するためのインターフェース（`IPBMAddressList`）を定義しています。
このインターフェースは、PBMトークンの適切な利用と管理を確保するため、ホワイトリスト（許可されたアドレス）とブラックリスト（禁止されたアドレス）の両方を操作する関数とイベントを提供します。

#### `isBlacklisted`
特定のアドレスがブラックリストに含まれているか確認する関数。
- **パラメータ**
    - `_address`
        - 確認したいアドレス。
- **戻り値**
    - `bool_`
        - アドレスがブラックリストに含まれていれば `true`、それ以外の場合は `false` を返します。

#### `isMerchant`
特定のアドレスが事業者のホワイトリストに含まれており、同時にブラックリストには含まれていないかを確認する関数。
- **パラメータ**
    - `_address`
        - 確認したいアドレス。
- **戻り値**
    - `bool_`
        - アドレスがホワイトリストに含まれ、ブラックリストに含まれていなければ `true`、それ以外の場合は `false` を返します。

#### `MerchantList`
事業者リストが編集されたときに発行されるイベント。
リストにアドレスが追加または削除されたときに通知を行います。
- **パラメータ**
    - `action`
        - 行われたアクションの種類（"add" または "remove"）。
    - `addresses`
        - 追加または削除された事業者のウォレットアドレスの配列。
    - `metadata`
        - 追加または削除に関するオプショナルなコメントや注記。

#### `Blacklist`
ブラックリストが編集されたときに発行されるイベント。
リストにアドレスが追加または削除されたときに通知を行います。
- **パラメータ**
    - `action`
        - 行われたアクションの種類（"add" または "remove"）。
    - `addresses`
        - 追加または削除されたウォレットアドレスの配列。
    - `metadata`
        - 追加または削除に関するオプショナルなコメントや注記。

#### 使用方法
このインターフェースを実装することで、PBMトークンのアンラップを制御するアドレスリストを柔軟に管理できます。
特定のアドレスが取引を行う資格があるかを確認するために、`isBlacklisted` や `isMerchant` などの関数が利用されます。
これにより、PBMトークンの安全な流通が保証され、不正行為や不適切な使用を防ぐことができます。

### 拡張

#### PBMRC1 - Token Receiver

:::details コード

```ts
pragma solidity ^0.8.0;

/// @notice Smart contracts MUST implement the ERC-165 `supportsInterface` function and signify support for the `PBMRC1_TokenReceiver` interface to accept callbacks.
/// It is optional for a receiving smart contract to implement the `PBMRC1_TokenReceiver` interface
/// @dev WARNING: Reentrancy guard procedure, Non delegate call, or the check-effects-interaction pattern must be adhere to when calling an external smart contract.
/// The interface functions MUST only be called at the end of the `unwrap` function.
interface PBMRC1_TokenReceiver {
    /**
        @notice Handles the callback from a PBM smart contract upon unwrapping
        @dev An PBM smart contract MUST call this function on the token recipient contract, at the end of a `unwrap` if the
        receiver smart contract supports type(PBMRC1_TokenReceiver).interfaceId
        @param _operator  The address which initiated the transfer (either the address which previously owned the token or the address authorised to make transfers on the owner's behalf) (i.e. msg.sender)
        @param _from      The address which previously owned the token
        @param _id        The ID of the token being unwrapped
        @param _value     The amount of tokens being transferred
        @param _data      Additional data with no specified format
        @return           `bytes4(keccak256("onPBMRC1Unwrap(address,address,uint256,uint256,bytes)"))`
    */
    function onPBMRC1Unwrap(address _operator, address _from, uint256 _id, uint256 _value, bytes calldata _data) external returns(bytes4);

    /**
        @notice Handles the callback from a PBM smart contract upon unwrapping a batch of tokens
        @dev An PBM smart contract MUST call this function on the token recipient contract, at the end of a `unwrap` if the
        receiver smart contract supports type(PBMRC1_TokenReceiver).interfaceId

        @param _operator  The address which initiated the transfer (either the address which previously owned the token or the address authorised to make transfers on the owner's behalf) (i.e. msg.sender)
        @param _from      The address which previously owned the token
        @param _id        The ID of the token being unwrapped
        @param _value     The amount of tokens being transferred
        @param _data      Additional data with no specified format
        @return           `bytes4(keccak256("onPBMRC1BatchUnwrap(address,address,uint256,uint256,bytes)"))`
    */
    function onPBMRC1BatchUnwrap(address _operator, address _from, uint256[] calldata _ids, uint256[] calldata _values, bytes calldata _data) external returns(bytes4);
}
```

:::

上記のSolidityインターフェース`IPBMRC1`は、Purpose Bound Money (PBM) トークンの基本的な機能を提供するためのものです。
これは、特定の目的にバインドされたマネーを管理するためのスマートコントラクトの構造を定義し、PBMトークンが**ERC1155**マルチトークンスタンダードに準拠しながら、追加のロジカル制約を適用する方法を示します。

##### `initialise`関数
コントラクトを初期化する関数。
これには、ベースとなる**ERC20**互換トークンのアドレス、コントラクトの全体的な有効期限、およびPBMラッパーロジックを含むスマートコントラクトのアドレスを指定します。
- **パラメータ**
    - `_sovToken`
        - ベースとなる**sovToken**のアドレス。
    - `_expiry`
        - コントラクトの全体的な有効期限（Unixエポック時間）。
    - `_pbmWrapperLogic`
        - PBMの条件を管理するスマートコントラクトのアドレス。

##### `uri`関数
指定されたトークンIDに対するメタデータURIを提供する関数。
これは**ERC1155**のメタデータURI JSONスキーマに準拠するJSONファイルを指す必要があります。

##### `safeMint`と`safeMintBatch`関数
新しいPBMトークンを作成し、指定されたレシーバー（受取人）アドレスへ`transfer`します。
これらの関数は、基礎となるトークンをPBMスマートコントラクトにラップしながら、新しいPBMトークンを発行します。
- **要件**
    - コントラクトは一時停止状態であってはならない。
    - トークンは有効期限切れであってはならない。
    - 発行者はPBMコントラクトにsovTokenの支出を承認している必要がある。
    - レシーバーはブラックリストに載っていないこと。

##### `burn`と`burnBatch`関数
PBMトークンを破棄する関数。
このプロセス中に、ラップされた基礎となるトークンは適切に処理されるべきです。
- **要件**
    - `from`アドレスはゼロアドレスであってはならない。
    - `from`は少なくとも指定された量のトークンを持っている必要があります。

##### `safeTransferFrom`と`safeBatchTransferFrom`関数
PBMトークンをあるウォレットから別のウォレットへ`trasnfer`する関数。
この関数は、特定の条件と制限を考慮しながらPBMトークンの自由な移動を可能にします。

##### `unwrap`関数
必要なPBM条件が満たされた場合に、ベースとなる**ERC20**互換トークンを特定のエンドポイント（例えば、ホワイトリストに登録された事業者）へ**unwrap**する関数。

##### `TokenUnwrapForTarget`、`TokenUnwrapForPBMBurn`、`TokenWrap`
これらのイベントは、トークンのラッピングとアンラッピングのプロセス中に発生し、トークンの動きを追跡するために重要です。

#### PBMRC2 - プリロードされていないPBMインターフェース

:::details コード

```ts
pragma solidity ^0.8.0;

/**
 *  @dev This interface extends IPBMRC1, adding functions for working with non-preloaded PBMs.
 *  Non-preloaded PBMs are minted as empty containers without any underlying tokens of value,
 *  allowing the loading of the underlying token to happen at a later stage.
 */
interface PBMRC2_NonPreloadedPBM is IPBMRC1 {

  /// @notice This function extends IPBMRC1 to mint PBM tokens as empty containers without underlying tokens of value.
  /// @dev The loading of the underlying token of value can be done by calling the `load` function. The function parameters should be identical to IPBMRC1
  function safeMint(address receiver, uint256 tokenId, uint256 amount, bytes calldata data) external;

  /// @notice This function extends IPBMRC1 to mint PBM tokens as empty containers without underlying tokens of value.
  /// @dev The loading of the underlying token of value can be done by calling the `load` function. The function parameters should be identical to IPBMRC1
  function safeMintBatch(address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data) external;

  /// @notice Wrap an amount of sovTokens into the PBM
  /// @dev function will pull sovTokens from msg.sender
  /// Approval must be given to the PBM smart contract in order to for the pbm to pull money from msg.sender
  /// underlying data structure must record how much the msg.sender has been loaded into the PBM.
  /// Emits {TokenLoad} event.
  /// @param amount    The amount of sovTokens to be loaded
  function load(uint256 amount) external;

  /// @notice Retrieves the balance of the underlying sovToken associated with a specific PBM token type and user address.
  /// This function provides a way to check the amount of the underlying token that a user has loaded into a particular PBM token.
  /// @param user The address of the user whose underlying token balance is being queried.
  /// @return The balance of the underlying sovToken associated with the specified PBM token type and user address.
  function underlyingBalanceOf(address user) external view returns (uint256);

  /// @notice Unloads all of the underlying token belonging to the caller from the PBM smart contract.
  /// @dev The underlying token that belongs to the caller (msg.sender) will be removed and transferred
  /// back to the caller.
  /// Emits {TokenUnload} event.
  /// @param amount The quantity of the corresponding tokens to be unloaded.
  /// Amount should not exceed the amount that the caller has originally loaded into the PBM smart contract.
  function unload(uint256 amount) external;

  /// @notice Emitted when an underlying token is loaded into a PBM
  /// @param caller Address by which sovToken is taken from.
  /// @param to Address by which the token is loaded and assigned to
  /// @param amount The quantity of tokens to be loaded
  /// @param sovToken The address of the underlying sovToken.
  /// @param sovTokenValue The amount of underlying sovTokens loaded
  event TokenLoad(address caller, address to, uint256 amount, address sovToken, uint256 sovTokenValue);

  /// @notice Emitted when an underlying token is unloaded from a PBM.
  /// This event indicates the process of releasing the underlying token from the PBM smart contract.
  /// @param caller The address initiating the token unloading process.
  /// @param from The address from which the token is being unloaded and removed from.
  /// @param amount The quantity of the corresponding unloaded tokens.
  /// @param sovToken The address of the underlying sovToken.
  /// @param sovTokenValue The amount of unloaded underlying sovTokens transferred.
  event TokenUnload(address caller, address from, uint256 amount, address sovToken, uint256 sovTokenValue);
}
```

:::

`PBMRC2_NonPreloadedPBM`インターフェースは、Purpose Bound Money (PBM) トークンのための拡張機能を提供します。
このインターフェースは、PBMトークンが初期的にはプリロードされず、値のある基礎となるトークン（sovToken）を後からトークンにロードするオプションを可能にすることで、非プリロードPBMの機能を実装します。
これにより、PBMは最初は空のコンテナとして発行され、後に任意のタイミングでsovTokenをロードできるようになります。

##### `safeMint` と `safeMintBatch`
`IPBMRC1`インターフェースの機能を拡張し、PBMトークンを値のない空のコンテナとして発行する関数。
ベースとなるトークンのロードは、`load` 関数を呼び出すことで後から行います。

##### `load`
指定した量のsovTokenをPBMにロードする関数。
この処理には、`msg.sender`からsovTokenをPBMスマートコントラクトに引き出すための事前承認が必要です。
ロードされたトークンの量は、内部データ構造に記録されます。

##### `underlyingBalanceOf`
特定のユーザーが特定のPBMトークンタイプにロードしたsovTokenの量を確認する関数。
これにより、ユーザーがPBMにどれだけのsovTokenを投入したかを確認できます。

##### `unload`
`msg.sender`が所有する、PBMからベースとなるトークンをアンロード（取り出し）し、それを`msg.sender`に返送す関数。
アンロードする量は、元々ロードされた量を超えてはなりません。

##### `TokenLoad`
PBMにsovTokenがロードされたときに発行されるイベント。
このイベントは、トークンのロード元とロード先、ロードされた量、および関連するsovTokenの詳細を記録します。

##### `TokenUnload`
PBMからsovTokenがアンロードされたときに発行されるイベント。
このイベントは、トークンのアンロードを開始したアドレス、アンロード元のアドレス、アンロードされた量、および関連するsovTokenの詳細を記録します。

## 補足

### なぜsovTokenはERC20互換である必要があるのか？

#### 通貨機能と安定性
PBMは通貨としての機能を持つことが想定されているため、安定した価値を持つ代替可能なトークンでなければなりません。
現在、市場にある主要なステーブルコインは主に**ERC20**インターフェイスに基づいています。

#### 広範なサポート
**ERC20**または**ERC20**互換トークンは、既存のウォレット、DeFiアプリケーション、そしてプロトコル設計（例えば**ERC4337**など）で最も広くサポートされており、実質的に代替可能なトークンのデファクトスタンダードとなっています。

:::message
**ERC4337**については以下の記事を参考にしてください。
:::

[\[ERC4337\] プロトコルの変更をせずにAAを実装する仕組みを理解しよう！](/articles/8c97ca7c93c4557ebfc2)

#### 他のスタンダードとの比較
- **ERC721**
    - 非代替可能なトークン（NFT）のためのスタンダードであり、通貨としての機能を果たすことはできません。
- **ERC1155**
    - このスタンダードは代替可能なトークンにも使用できますが、市場に**ERC1155**ベースのステーブルコインが少ないため、採用されませんでした。
    - また、基本的に小数点をサポートしていない点もありますが、これは致命的な問題ではありません。

#### なぜPBMラッパーはERC1155互換である必要があるのか？

- **複数トークンサポート**
    - **ERC1155**は単一のコントラクトで複数のトークンタイプをサポートできます。
    - これは、異なる額面、有効期限、ビジネスロジックを持つトークンの発行を一つのコントラクトでサポートするため、PBMのユースケースに非常に便利です。

- **バッチ転送のサポート**
    - **ERC1155**はバッチ`trasnfer`をサポートしており、これにより大量の受取アドレスにトークンをエアドロップする場合などにガス代を節約できます。

- **セミファンジブルトークンのサポート**
    - PBMの場合、有効期限後にコレクティブル（収集可能なアイテム）に変換できる可能性があるため、セミファンジブルトークンのサポートは非常に便利です。

- **ウォレットUIでの視覚化**
    - **ERC1155**はウォレットのUIでPBMトークンを視覚的に表示する機能を提供します。
    - これにより、既存のウォレットプロバイダがこれらのスタンダードをサポートすることが容易になり、カスタムユーザーインターフェイスの開発コストと市場投入までの時間を短縮することができます。

上記のテキストは、Purpose Bound Money（PBM）がなぜ特定の宛先アドレスのホワイトリストとブラックリストを必要とするのかについて説明しています。これは、PBMが目的に束縛されたマネーであるため、定義された条件がすべて満たされた場合にのみ解包（アンラップ）されるべきだからです。以下、詳しく解説します。

### なぜホワイトリストが必要か？

#### 目的限定の実装
PBMは目的限定マネーであるため、あらかじめ定義されたホワイトリストに含まれる宛先へのみ解包が許可されます。
これにより、PBMの使用がその目的に沿って行われることが保証されます。
   
#### 動的条件の統合
開発者は、ホワイトリストがランタイムで動的に決定されるように設定することもできます。
例えば、ウォレットアドレスに特定のNFTが存在するか、オラクルに依存するなどの条件がこれに該当します。

### なぜブラックリストが必要か？

#### セキュリティと規制の遵守
様々な理由（例えば、アカウントの再登録、苦情や法的な理由によるアカウントの停止・撤退・追放など）で禁止されたアカウントを排除するために、ブラックリストが必要です。

#### なぜホワイトリストだけではなく、ブラックリストも必要か？

- **ガス効率と実用性
    - ホワイトリストのみを使用すると同じ効果が得られるかもしれませんが、一人を禁止するためにホワイトリストをブロックチェーンに再デプロイすることはガス効率が良くありません。
    - また、ブラックリストのみを使用して目的限定マネーを実装することは、除外するすべてのアドレスをリストアップし、新しいアカウントが作成されるたびに更新する必要があるため、現実的ではありません。

#### なぜ宛先が必要か？

- **コアプロポーザル**
    - 提案の核心は、PBMが事前承認された宛先に転送されたときにのみ解包できるということです。
    - PBMは自由に転送できますが、基礎となるsovTokenを受け取って解包できる対象は、普通のステーブルコインと区別するために限定される必要があります。

### ビジネスロジックには何が含まれるのか？

ビジネスロジックが一般的にどのように構成されるか、そしてそれがどのようにPurpose Bound Money (PBM) システムに適用されるかについて述べています。
ビジネスロジックは、主にコアロジック、プラグインロジック、フックロジックの三つに分けられます。

#### コアロジック
- **基本機能と検証チェック**
    - これには必須の機能とバリデーションチェックが含まれ、PBMラッパーコントラクトに含めるべきです。
    - このロジックは、PBMが基本的な要件を満たすことを保証し、システムの基盤となる部分です。

#### プラグインロジック
- **追加機能の拡張**
    - コアロジックを拡張する形で機能を追加します。
    - 例えば、カスタムデータ収集や追加の管理機能などがこれに該当します。
    - プラグインロジックは独立したスマートコントラクトモジュールに含まれ、PBMラッパーコントラクトによって登録されます。

#### フックロジック
- **追加のバリデーションチェック**
    - 特定のPBMのサブセットにのみ適用される追加のバリデーションチェックを実装します。
    - これもプラグインと同様に、独立したスマートコントラクトモジュールに含まれることが多いです。

#### PBMビジネスロジックの内容
**アクセス制御ロジック**、**PBMunwrapロジック**、**非ブロックチェーンITシステムとの統合を目的としたAPIロジック**などが含まれます。
PBMは、政府の支出トークン、ショッピングバウチャー、プリペイドトークン、報酬ポイントトークン、目的限定の寄付トークン、学校の手当トークンなど、幅広いユースケースに使用されるため、各ユースケースごとに異なるビジネスロジックが必要になります。

このため、ビジネスロジックは意図的に未定義にされており、実装者が最大限の柔軟性を持てるようになっています。
このアプローチにより、PBMシステムはさまざまな要件や状況に適応しやすくなります。

### トランザクションモデルの選択理由

この標準では、送信者側からトランザクションを開始する「**プッシュトランザクションモデル**」が採用されています。
これは、現代のウォレットが**ERC1155**の`safeTransfer`関数内に**unwrap**ロジックを組み込むことで必要なPBMロジックをサポートできるためです。
送信者がアクティブにトランザクションを制御することで、より安全で意図したトランザクションが実行されることが保証されます。

### カスタマイズ性

**ERC1155** PBMトークンは、PBMTokenというデータ構造にマッピングされ、実装者はビジネスロジックに応じて自由にカスタマイズできます。
これにより、同一のスマートコントラクト内で複数のトークンタイプを効率的に管理し、各トークンタイプに対して複数の条件付き**unwrap**ロジックを持たせることが可能になります。
これは、各トークンタイプごとに複数のスマートコントラクトをデプロイする必要がないため、ガスコストの削減にもつながります。

#### メタデータの管理

PBM標準では、メタデータを直接含まないようにしています。
必要に応じて、別のメタデータ拡張インターフェース（例：**ERC721**の`ERC721Metadata`）を使用して関連メタデータを作成できます。
実装例としては、Openseaのメタデータ基準が参考になります。

#### 拡張性

将来の拡張性を考慮し、開発者にはメソッド動作の一般的な拡張性を構築するための仕様（**ERC5750**）を読み、採用することが推奨されています。
これにより、PBMアーキテクチャは意図的にモジュラーであるため、スマートコントラクトのカスタマイズ性と再利用性が向上します。

:::message
**ERC5750**については以下の記事を参考にしてください。
:::

[\[ERC5750\] 各メソッドの引数にデータを追加して動作を拡張する仕組みを理解しよう！](/articles/216265154b75c1612904)

#### アクセス制御と実行条件

このEIPは、アクセス制御や関数が実行可能な条件についての仮定は設けていません。
PBMの各ビジネスフローに関与するさまざまな役割を決定するのは、PBMの作成者の責任です。

## 互換性

このインターフェースは**ERC1155**と互換性があるように設計されています。

## 実装

以下のページに格納されています。

https://eips.ethereum.org/assets/eip-7291/

## セキュリティ

#### 公開情報とガス制限
 スマートコントラクト内のすべての使用される要素は公開されていて、ローカル変数やprivateとマークされた状態変数も含まれます。
ガス制限のため、固定されていない反復回数のループは慎重に使用する必要があります。

#### 認証とデリゲートコール
認証チェックには`tx.origin`ではなく`msg.sender`を使用するべきです。
ライブラリコードを`delegatecall`の一部として使用する場合、ライブラリコードはステートレスである必要があります。
これにより、悪意のあるアクターが`delegatecall`を通じてコントラクトの状態を変更することを防ぎます。

#### トランザクションの安全性
悪意のあるアクターによるトランザクションの先行実行を防ぐために、コミットメントスキームを使用することが推奨されます。
`block.timestamp`はエントロピー源や乱数として使用しないでください。

#### 署名とリプレイ攻撃
オフチェーンでのメッセージ署名は有効な技術ですが、同じ署名が悪用されて複数回の関数実行に利用される可能性があります。
これを防ぐために、メッセージは`nonce`とコントラクトのアドレスで署名されるべきです。

#### PBMトークンの不正利用リスク
再入力攻撃による二重支出、PBMトークンのクローン作成、無効なPBMトークンの作成、または価値のないsovTokenをラッピングしてPBMトークンの額面価値を偽造することが懸念されます。

#### コントラクトの一貫性と監査
コントラクトが停止された場合やユーザーのトークン`transfer`が不正な活動や誤送付の疑いで制限される場合、対応する制限がユーザーのPBMトークンの**unwrap**リクエストにも適用されるべきです。
セキュリティ監査とテストを実施して、**unwrap**ロジックが期待通りに動作するか、または複雑なビジネスロジックが外部スマートコントラクトを呼び出す場合の再入力攻撃やその他の呼び出し連鎖攻撃を防ぐ必要があります。

#### PBMトークンの燃焼とアクセス制御
トークンの`burn`は通常、ホワイトリストに登録された事業者またはPBM所有者に限定されるべきです。
これにより、有効期限前後のトークン処理が適切に管理されます。

これらのセキュリティ考慮事項は、PBMシステムを設計し実装する際に重要な役割を果たし、システムが安全に運用されるための基盤を提供します。

## 引用

Orchid-Dev (@proj-orchid-straitsx), Victor Liew (@alcedo), Wong Tse Jian (@wongtsejian), Jacob Shan (@Jacobshan429), Chin Sin Ong (@chinsinong), "ERC-7291: Purpose bound money [DRAFT]," Ethereum Improvement Proposals, no. 7291, June 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7291.

## 最後に

今回は「**ERC20**互換のデジタルマネートークンと**ERC1155**互換のスマートコントラクトを使用して、取引の条件が満たされた時に自動的に支払いを行う、PBM（Purpose Bound Money）という仕組みであるを提案している**EIP7291**」についてまとめてきました！
いかがだったでしょうか？

質問などがある方は以下のTwitterのDMなどからお気軽に質問してください！

[Twitter @cardene777](https://twitter.com/cardene777)

他の媒体でも情報発信しているのでぜひ他も見ていってください！

https://chaldene.net/

https://zenn.dev/heku

https://cardene.substack.com/

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58

https://cardene.notion.site/ERC-EIP-2a03fa3ea33d43baa9ed82288f98d4a9?pvs=4
