# [ERC6808] FKBTと呼ばれるERC20トークンをより安全に送付・管理する仕組みを理解しよう！

> FKBT（Fungible Key Bound Tokens）は、スマートコントラクト内で使用されるトークンの一種で、Key Bound Tokens（KBT）というカテゴリーに属します。

- URL: https://cardene.dev/articles/77f7104857ce86ee70c7
- 公開日: 2023-09-21
- 更新日: 2023-09-21
- トピック: Blockchain
- タグ: ethereum, solidity, ブロックチェーン, nft

## はじめに

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

https://cryptogames.co.jp/

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

https://cryptospells.jp/

今回は、**FKBT**と呼ばれる**Fungible Key Bound Tokens**という、**ERC20**トークンの送付をより安全に実行し管理する規格を提案している**ERC6808**についてまとめていきます！

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

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

:::message alert
**6808**は現在（2023年9月16日）では「**Last Call**」段階です。
:::

## 概要

FKBT（Fungible Key Bound Tokens）は、スマートコントラクト内で使用されるトークンの一種で、Key Bound Tokens（KBT）というカテゴリーに属します。
FKBTは、トークンに関する標準的なルールやインターフェースを提供し、特に`addBindings`という関数で基本的な機能を提供します。
`addBindings`関数は、**Key Wallets**と呼ばれる特定のウォレットを指定することで、安全なトークンの転送を実現します。
このプロセスによって、FKBTはユーザーまたはオンチェーンの第三者エンティティによって安全に承認され、その後、使用できるようになります。

FKBTの主要なアイデアは、セキュリティ機能を完全に選択可能なものとして提供し、それをファンジブルなアセット自体に組み込むことです。
このセキュリティ機能は、`allowTransfer`（転送を許可する）や`allowApproval`（承認を許可する）という関数を通じて実現されます。
これらの関数は、**Key Wallets**の中から呼び出され、**Holding Wallet**が**ERC20**トークンの`transfer`や`approve`関数を呼び出すかどうかを許可します。
これにより、FKBTの責任が分散されます。
**Holding Wallet**はアセットを保持し、**Key Wallets**はアセットの使用方法や承認方法に権限を持っています。
伝統的なファンジブル**ERC20**のデフォルトの動作は、`addBindings`関数を使用しないことで実現できます。

FKBTは、ファンジブルなアセットに追加のセキュリティを求める個人や、第三者のウォレット、ブローカー、銀行、保険会社など、さまざまな利用者に向けて設計されています。
FKBTはアセット自体をセルフカストディアル（自己保管）レベルで保護することにより、攻撃や盗難に対して強固なセキュリティを提供します。

## 用語

#### Key Wallet/s (キーウォレット)
`_keyWallet1`または`_keyWallet2`のいずれかを指定します。
これらのウォレットは、`safeFallback`、`resetBindings`、`allowTransfer`、`allowApproval`関数を呼び出すことができます。

#### Safe Transfer (安全な転送)
**Key Wallets**の1つがFKBTの使用を安全に承認した場合に発生します。

#### Holding Wallet (保有ウォレット)
FKBTを保持しているウォレット。

#### Default Behavior/s (デフォルトの動作)
既存の非代替トークン（**ERC20**）標準をデフォルトで使用しています。

#### Crypto Scam Reports (暗号詐欺の報告数)
2021年1月から2022年3月までにアメリカ合衆国連邦取引委員会が受け取った暗号詐欺の報告数。

#### Amount Stolen (盗まれた金額)
2021年1月から2022年3月までにアメリカ合衆国連邦取引委員会によって報告された暗号詐欺によって盗まれた金額。

#### Default Value/s (デフォルトの値)
非代替トークン（**ERC20**）のデフォルトの値。

#### Amount (金額)
使用する予定のFKBTの金額。

#### Time in allowTransfer (allowTransfer内の時間)
`allowTransfer`内の`_time`は、転送が行われるブロック数を表します。

#### Address (_address)
FKBTが送信されるアドレス。

#### allFunds (_allFunds)
`true`または`false`に設定できるブール値。

#### Time in allowApproval (allowApproval内の時間)
`allowApproval`内の`_time`は、承認が行われるブロック数を表します。

#### Number of Transfers (_numberOfTransfers)
ユーザーの代わりにサードパーティエンティティが転送できる回数。

#### _keyWallet1
`addBindings`関数を呼び出す時に設定される2つの**Key Wallets**の1つ。

#### _keyWallet2
`addBindings`関数を呼び出す時に設定される2つの**Key Wallets**の1つ。

#### PoS Protocol (PoSプロトコル)
暗号通貨のコンセンサスメカニズムで、トランザクションの処理と新しいブロックの生成に使用されます。

## 動機

現代の急速に進化するテクノロジーの世界では、人々は異なる速さで学び成長します。
この状況下で暗号資産の普及を考える時、対象となる人々がさまざまな年齢やバックグラウンドを持つことを考慮する必要があります。
暗号資産の自己保有型ウォレットは、自分自身で責任を持ってセキュリティを確保しなければならないという利点と、誤った操作によって資産を失った場合、誰も保証してくれないという欠点を同時に持っています。

2021年1月から2022年3月まで、アメリカ合衆国連邦取引委員会は`46,0005`件以上の暗号詐欺の報告を受けました。
これにより、暗号通貨利用者に直接影響があり、消費者損失は10億ドル以上に上りました。
このような詐欺や悪質な行為は金融セクター全般で問題となり、厳しい規制が導入されることがあります。
しかし、Web3やBlockchainという分野の価値観として、政府による規制は適切でないという考えもあります。
そのため、セキュリティを向上させるために、中央集権的および分散型の方法が模索されていますが、両者の利点を保ちつつ欠点を排除する解決策は提供されていませんでした。

提案者は、過去に多くの人々が自問自答した質問と同じ質問をしました。
それは、「**ウォレットをどのように保護するか？**」という質問ではなく、「**資産をどのように保護するか？」**という質問です。
ウォレットの作成は無料であり、価値があるのはその中に保管される資産です。
この問いから、KBT（Key Bound Tokens）が開発されました。
これは完全に選択肢のある解決策であり、ユーザーが自分自身に合わせてカスタマイズできます。
セキュリティ機能がアクティブである限り、シードフレーズやプライベートキーが公に公開されても、個々の資産は保護されます。

FKBTは、広く使用されている**ERC20**トークンの標準を改善する必要性があると判断しました。
ファンジブルアセットのセキュリティは、暗号通貨スペース内のすべてのエンティティに関連するトピックです。
FKBTは、ウォレットのセキュリティに焦点を当てるだけでなく、トークンのセキュリティにも焦点を当て、スケーラブルで分散型のセキュリティソリューションを提供します。
このセキュリティはブロックチェーン自体に存在し、インターネットへのアクセス権を持つあらゆる人が、現在のハードウェアや中央集権的なソリューションを必要とせずに資産を保護できるようになります。
FKBTは有望な代替案として設計され、**ERC20**のすべての特性を受け継いでいます。
これにより、FKBTは伝統的なファンジブルトークンを使用する設定にしたすべてのdAppで使用できるようになりました。

KBT（Key Bound Tokens）の開発プロセスにおいて、以下の利点がモチベーションとなっています。

#### 完全に分散化されている
KBTのセキュリティ機能は完全に分散化されており、アクティブ化された場合でも第三者はユーザーの資産にアクセスできません。
これは、自己管理型の資産の原則に合致するために実現され、ユーザーは完全に自分の資産を管理できます。

#### 無限のスケーラビリティ
中央集権的な解決策ではアカウントの作成が必要で、場所によっては利用が制限されることがありますが、KBTは地域制限やアカウント作成の制約を受けません。
さらに、分散型セキュリティソリューションの中にはスケーラビリティの問題を抱えるものもありますが、KBTはインターネットへのアクセスがあれば世界中で利用できます。

#### 完全に選択肢のあるセキュリティ
KBTのセキュリティ機能はオプションで、ユーザーがカスタマイズして取り外すことができます。
ユーザーは自身のセキュリティのレベルを自由に選択できます。

#### デフォルトの機能性
ユーザーがKBTを従来の**ERC20**トークンとして使用したい場合、セキュリティ機能をアクティブにする必要はありません。
トークンは従来のファンジブルなトークンと同じ特性を持っており、セキュリティ機能がアクティブである場合でも、ユーザーはそれをカスタマイズできます。
ユーザーはカスタム値やデフォルト値を設定することも、dAppを通じてそれらを渡すこともできます。

#### セキュリティ
KBTは`addBindings`関数を介してセキュリティを強化し、トークンの移動や承認に対してキーウォレットが必要です。
これにより、従来の**ERC20**に存在する多くの障害を防ぐことができます。

#### セキュリティの予防策
KBTを使用すると、**Holding Wallet**や**Key Wallet**のいずれかが漏洩した場合でも、ユーザーはトークンが安全であることを確信できます。
**Holding Wallet**が漏洩した場合、ユーザーは**Key Wallet**から`safeFallback`関数を呼び出し、資産を別の**Key Wallet**に移動させることができます。
また、**Key Wallet**が漏洩した合場合、`resetBindings`関数を呼び出すことでセキュリティ機能をリセットし、新しい**Key Wallet**を追加して単一の障害点を防ぐことができます。

#### 匿名のセキュリティ
KBTを使用する際、ユーザーは個人情報を提供する必要がなく、すべての操作はブロックチェーン上で匿名で行えます。
プライバシーと資産の匿名性を保護します。

#### 低コストのセキュリティ
KBTのセキュリティ機能のコストはオンチェーン手数料（**GWEI**）に関連しており、多くの人々にとってコスト効果のあるセキュリティ対策です。

#### 環境にやさしい
KBTのセキュリティ機能はブロックチェーンにコード化されており、中央集権的なサーバーや物理的なオブジェクトの生産が不要です。
これにより、環境に対する影響が最小限に抑えられます。

#### ユーザーエクスペリエンス
セキュリティ機能は`addBindings`関数を呼び出すだけで簡単にアクティブ化できます。
ユーザーは`_keyWallet114`と`_keyWallet215`として機能する2つの他のウォレットが必要です。
オプションのセキュリティ機能は、ユーザーエクスペリエンスとEthereumエコシステム全体を向上させ、セキュリティを重視するユーザーに安全な手段を提供します。

## 仕様

### IKBT20（トークンコントラクト）

以下の仕様は、**Solidity 0.8.0**（またはそれ以降のバージョン）の構文を使用しています。

関数呼び出しの結果として「`false`」が返ってくる可能性がある場合、呼び出し元のコードはそれに対処する必要があります。
また、呼び出し元は関数呼び出しの成功または失敗を確認し、適切に対処する必要があります。

関数呼び出しの結果が常に成功するわけではなく、失敗（`false`）が返される可能性があり、それに対処する必要があります。

```javascript
interface IKBT20 {
    event AccountSecured(address _account, uint256 _amount);
    event AccountResetBinding(address _account);
    event SafeFallbackActivated(address _account);
    event AccountEnabledTransfer(
        address _account,
        uint256 _amount,
        uint256 _time,
        address _to,
        bool _allFunds
    );
    event AccountEnabledApproval(
        address _account,
        uint256 _time,
        uint256 _numberOfTransfers
    );
    event Ingress(address _account, uint256 _amount);
    event Egress(address _account, uint256 _amount);

    struct AccountHolderBindings {
        address firstWallet;
        address secondWallet;
    }

    struct FirstAccountBindings {
        address accountHolderWallet;
        address secondWallet;
    }

    struct SecondAccountBindings {
        address accountHolderWallet;
        address firstWallet;
    }

    struct TransferConditions {
        uint256 amount;
        uint256 time;
        address to;
        bool allFunds;
    }

    struct ApprovalConditions {
        uint256 time;
        uint256 numberOfTransfers;
    }

    function addBindings(
        address _keyWallet1,
        address _keyWallet2
    ) external returns (bool);

    function getBindings(
        address _account
    ) external view returns (AccountHolderBindings memory);

    function resetBindings() external returns (bool);

    function safeFallback() external returns (bool);

    function allowTransfer(
        uint256 _amount,
        uint256 _time,
        address _to,
        bool _allFunds
    ) external returns (bool);

    function getTransferableFunds(
        address _account
    ) external view returns (TransferConditions memory);

    function allowApproval(
        uint256 _time,
        uint256 _numberOfTransfers
    ) external returns (bool);

    function getApprovalConditions(
        address account
    ) external view returns (ApprovalConditions memory);

    function getNumberOfTransfersAllowed(
        address _account,
        address _spender
    ) external view returns (uint256);

    function isSecureWallet(address _account) external view returns (bool);
}
```

### イベント

#### AccountSecured

```javascript
AccountSecured(address _account, uint256 _amount)
```

**概要**

`_accountがaddBindings`関数を呼び出してアカウントをセキュアにする時に発行されるイベント。

**詳細**

- `_account`
    - アカウントのアドレス。
- `_amount`
    - アカウントの現在の残高。

---

#### AccountResetBinding

```javascript
AccountResetBinding(address _account)
```

**概要**

ホルダーが`resetBindings`関数を呼び出してキーウォレットをリセットする時に発行されるイベント。

**詳細**

- `_account`
    - アカウントのアドレス。

---

#### SafeFallbackActivated

```javascript
SafeFallbackActivated(address _account)
```

**概要**

ホルダーが`safeFallback`関数を呼び出してすべての資産をキーウォレットの1つに移動する時に発行されるイベント。

**詳細**

- `_account`
    - アカウントのアドレス。

---

#### AccountEnabledTransfer

```javascript
AccountEnabledTransfer(address _account, uint256 _amount, uint256 _time, address _to, bool _allFunds)
```

**概要**
`_accountがallowTransfer`関数を呼び出してトークンの転送を許可した時に発行されるイベント。

**詳細**
`_time`で指定されたブロック秒間で`_amount`で指定されたトークン量を`_to`アドレスに転送を許可した時に発行されます。
また、`_allFunds`が`true`に設定されている場合、すべての資産の転送を許可した場合も発行されます。

**パラメータ**

- `_account`
    - アカウントのアドレス。
- `_amount`
    - 転送が許可されたトークンの量。
- `_time`
    - ブロック秒数で表される転送の許可期間。
- `_to`
    - 転送が許可された宛先アドレス。
- `_allFunds`
    - すべての資産の転送が許可されている場合は`true`、それ以外は`false`。

---

#### AccountEnabledApproval

```javascript
AccountEnabledApproval(address _account, uint256 _time, uint256 _numberOfTransfers)
```

**概要**

`_account`が`allowApproval`関数を呼び出して`_time`ブロック秒間の許可を設定し、`_numberOfTransfers`回の転送を許可した時に発行されるイベント。

**詳細**

- `_account`
    - アカウントのアドレス。
- `_time`
    - ブロック秒数で表される許可の期間。
- `_numberOfTransfers`
    - 転送が許可された回数。

---

#### Ingress

```javascript
Ingress(address _account, uint256 _amount)
```

**概要**

`_account`がホルダーになった時に発行されるイベント。
`_amount`はアカウントの現在の残高です。

**詳細**

- `_account`
    - アカウントのアドレス。
- `_amount`
    - アカウントの現在の残高。

---

#### Egress

```javascript
Egress(address _account, uint256 _amount)
```

**概要**

`_account`がすべてのトークンを転送し、ホルダーでなくなった時に発行されるイベント。
`_amount`はアカウントの以前の残高です。

**詳細**

- `_account`
    - アカウントのアドレス。
- `_amount`
    - アカウントの以前の残高。

### 関数

#### addBindings

```javascript
function addBindings(address _keyWallet1, address _keyWallet2) external returns (bool)
```

**概要**

送信者のアカウントを`_keyWallet1`と`_keyWallet2`と呼ばれる2つのウォレットでセキュアにし、`AccountSecured`イベントを発行するイベント。

**詳細**

この関数は以下の条件でリバートする必要があります。
- 送信者のアカウントがホルダーでない場合。
- 送信者が既にセキュアである場合。
- `keyWallets`が同じ場合。
- `keyWallets`の1つが送信者と同じ場合。
- 1つまたは両方の`keyWallets`がゼロアドレス（`0x0`）の場合。
- 1つまたは両方の`keyWallets`がすでに別のホルダーアカウントの`keyWallets`である場合。

**引数**
- `_keyWallet1`
    - 1つ目のキーウォレットのアドレス。
- `_keyWallet2`
    - 2つ目のキーウォレットのアドレス。

**戻り値**
- セキュリティの追加が成功した場合は`true`、それ以外の場合は`false`。

---

#### getBindings

```javascript
function getBindings(address _account) external view returns (AccountHolderBindings memory)
```

**概要**

`_account`のキーウォレットを`struct`形式で返す関数。

**引数**
- `_account`
    - アカウントのアドレス。

**戻り値**
- `AccountHolderBindings`構造体を返します。

---

#### resetBindings

```javascript
function resetBindings() external returns (bool)
```

**概要**

キーウォレットから呼び出され、ホルダーアカウントのキーウォレットをリセットし、`AccountResetBindingイベント`を発行する関数。

**詳細**
2つのキーウォレットのうち1つが漏洩した場合に役立ちます。
この関数は以下の条件でリバートする必要があります。
- 送信者がキーウォレットでない場合。

**戻り値**
- バインディングのリセットが成功した場合は`true`、それ以外の場合は`false`。

---

#### safeFallback

```javascript
function safeFallback() external returns (bool);
```

**概要**
キーウォレットから呼び出され、ホルダーアカウントからすべてのトークンを他のキーウォレットに転送し、`SafeFallbackActivated`イベントを発行するイベント。

**詳細**
ホルダーアカウントが漏洩した場合に役立ちます。
この関数は以下の条件でリバートする必要があります。
- 送信者がキーウォレットでない場合。

**戻り値**
- セーフフォールバックが成功した場合は`true`、それ以外の場合は`false`。

#### allowTransfer

```javascript
function allowTransfer(uint256 _amount, uint256 _time, address _to, bool _allFunds) external returns (bool);
```

**概要**

指定された金額を特定の期間、特定のアドレスに転送することを許可する関数。

**詳細**
- キーウォレットから呼び出され、転送関数が呼び出される前に呼び出されます。
- `_amount`が`0`の場合、金額に制限はありません。
- `_time`が`0`の場合、時間に制限はありません。
- `_to`アドレスがゼロアドレスの場合、`_to`アドレスに制限はありません。
- また、`_allFunds`が`true`の場合、他のパラメータに関係なく、すべての資産をいつでも誰にでも転送することを許可します。
- この関数は`AccountEnabledTransfer`イベントを発行します。

この関数は以下の条件でリバートする必要があります。
- 送信者がキーウォレットでない場合。
- `_amount`がホルダーアカウントの残高を超える場合。

**引数**
- `_amount`
    - 転送するトークンの量。
- `_time`
    - 転送を許可する期間（ブロック秒数で表す）。
- `_to`
    - 転送先アドレス。
- `_allFunds`
    - すべての資産の転送を許可する場合は`true`、それ以外は`false`。

**戻り値**
- 転送が成功した場合は`true`、それ以外の場合は`false`。

---

#### getTransferableFunds

```javascript
function getTransferableFunds(address _account) external view returns (TransferConditions memory);
```

**概要**

指定された`_account`の転送条件を取得する関数。

**詳細**

指定されたアカウントの転送条件を取得するために使用されます。
転送条件は、転送する金額 (`amount`)、転送可能な時間 (`time`)、転送先のアドレス (`to`)、およびすべての資金を転送するかどうかを示すブール値 (`allFunds`) の情報を含む構造体 (`TransferConditions`) で返されます。

**引数**

- `_account`
	- 取得する転送条件の対象となるアカウントのアドレス。

**戻り値**

- `TransferConditions`
	- 転送条件を表す構造体。
    - `amount`
        - 転送する金額。
    - `time`
        - 転送可能な時間。
    - `to`
        - 転送先のアドレス。
    - `allFunds`
        - すべての資金を転送するかどうかを示すブール値。

---

#### allowApproval

```javascript
function allowApproval(uint256 _time, uint256 _numberOfTransfers) external returns (bool);
```

**概要**

指定された`_time`の間、指定された`_numberOfTransfers`回数の転送を許可し、`spender`が許可された転送回数を制限する関数。

**詳細**

この関数は、キーウォレットから呼び出され、転送許可条件を設定します。
キーウォレットから呼び出され、`approve`、`increaseAllowance`、`decreaseAllowance`のいずれかが呼び出される前に呼び出されます。
指定された`_time`の期間中には、`spender`が指定された`_numberOfTransfers`回数までの転送を許可します。
これにより、`spender`がアカウントから行う転送の回数が制限されます。
また、この関数は`AccountEnabledApproval`イベントを発行し、`sender`がキーウォレットでない場合には`revert`するべきです。

**引数**

- `_time`
	- 転送を許可する期間を示すタイムスタンプ。
- `_numberOfTransfers`
	- 転送を許可する最大回数。
	- `0`の場合は無制限の転送が許可されます。

**戻り値**

- `bool`
	- 成功した場合は`true`、それ以外の場合は`false`を返します。

---

#### getApprovalConditions

```javascript
function getApprovalConditions(address _account) external view returns (ApprovalConditions memory);
```

**概要**

指定された`_account`の承認条件を取得する関数。

**詳細**

指定されたアカウントの承認条件を取得するために使用されます。
承認条件は、`approve`、`increaseAllowance`、`decreaseAllowance`関数が呼び出し可能なブロックタイムスタンプ (`time`) および`spender`が許可される転送回数 (`numberOfTransfers`) の情報を含む構造体 (`ApprovalConditions`) で返されます。

**引数**

- `_account`
	- 承認条件を取得する対象となるアカウントのアドレス。

**戻り値**

- `ApprovalConditions`
    - `time`
        - `approve`、`increaseAllowance`、`decreaseAllowance`関数が呼び出し可能なブロックタイムスタンプ。
    - `numberOfTransfers`
        - `spender`が許可される転送回数。

---

#### transfer

```javascript
function transfer(address _to, uint256 _amount) external returns (bool);
```

**概要**

指定された`_to`アドレスに`_amount`量のトークンを転送する関数。

**詳細**

`sender`のアカウント残高に十分なトークンがない場合、または`sender`が`allowTransfer`関数を介して資金の転送を許可していない場合は`revert`します。
また、この関数は`Transfer`イベントを発行します。
`0`の値を転送する場合でも、通常のトークン転送として扱います。

**引数**

- `_to`
	- トークンを転送するアドレス。
- `_amount`
	- 転送するトークンの量。

**戻り値**

- `bool`
	- 成功した場合は`true`、それ以外の場合は`false`を返します。

---

#### approve

```javascript
function approve(address _spender, uint256 _amount) external returns (bool);
```

**概要**

`_spender`に対して`_amount`量のトークンを複数回転送することを許可する関数。

**詳細**

`sender`アカウントがセキュアであり、`allowApproval`関数を呼び出していない場合、または`allowApproval`関数で設定された `_time`の経過時間がある場合は`revert`します。
また、この関数はApprovalイベントを発行し、既存の`allowance`を`_value`および`allowApproval`関数で設定された`_numberOfTransfers`と共に上書きします。

**引数**

- `_spender`
	- トークンを転送する対象のアドレス。
- `_amount`
	- 許可するトークンの量。
	- `0`の場合、`spender`は複数回転送することができます。

**戻り値**

- `bool`
	- 成功した場合は`true`、それ以外の場合は`false`を返します。

---

#### increaseAllowance

```javascript
function increaseAllowance(address _spender, uint256 _addedValue) external returns (bool);
```

**概要**

`_spender`に対する引き出し許可を増やす関数。

**詳細**

`sender`アカウントがセキュアであり、`allowApproval`関数を呼び出していない場合、`spender`がゼロアドレス (`0x0`) の場合、`allowApproval`関数で設定された`_time`の経過時間がある場合は`revert`します。
また、この関数は`Approval`イベントを発行し、引数`_addedValue`で指定された値を現在の`allowance`に追加します。

**引数**

- `_spender`
	- トークンを転送する対象のアドレス。
- `_addedValue`
	- `allowance`を増やすトークンの量。

**戻り値**

- `bool`
	- 成功した場合は`true`、それ以外の場合は`false`を返します。

---

#### decreaseAllowance

```javascript
function decreaseAllowance(address _spender, uint256 _subtractedValue) external returns (bool);
```

**概要**

`_spender`に対する引き出し許可を減らす関数。

**詳細**

`sender`アカウントがセキュアであり、`allowApproval`関数を呼び出していない場合、`spender`がゼロアドレス (`0x0`) の場合、`allowApproval関数で設定された`_time`の経過時間がある場合は`revert`します。
また、この関数は`Approval`イベントを発行し、引数`_subtractedValue`で指定された値を現在の`allowance`から減算します。
ただし、減算後の`allowance`が負の値にならないことを確認します。

**引数**

- `_spender`
	- トークンを転送する対象のアドレス。
- `_subtractedValue`
	- `allowance`から減算するトークンの量。

**戻り値**

- `bool`
	- 成功した場合は`true`、それ以外の場合は`false`を返します。

---

#### transferFrom

```javascript
function transferFrom(address _from, address _to, uint256 _amount) external returns (bool);
```

**概要**

指定された`_from`アドレスから`_to`アドレスに`_amount`量のトークンを転送する関数。

**詳細**

`sender`が明示的に承認した場合でなければ`revert`すべきです。
`spender`が関数を呼び出すたびに、合計転送回数が`0`に達した場合、`approve`を`0`の量で呼び出して承認が取り消されます。
`0`の値を転送する場合でも、通常のトークン転送として扱い、`Transfer`イベントを発行します。

**引数**

- `_from`
	- トークンを転送する元のアドレス。
- `_to`
	- トークンを転送する先のアドレス。
- `_amount`
	- 転送するトークンの量。

**戻り値**
- `bool`
	- 成功した場合は`true`、それ以外の場合は`false`を返します。

## 補足

### 一貫性と後方互換性の維持
FKBTの開発では、既存の**ERC20**トークンとの一貫性を重視しました。
これは、FKBTが**ERC20**トークンと同じように動作することを意味します。
これにより、dAppsなどのプラットフォームで既存の**ERC20**トークンからFKBTに移行する際に、シームレスな遷移が可能となりました。

### 自己保管のセキュリティ機能の提供
FKBTは、ユーザーに自己保管のセキュリティ機能を提供します。
これは、ユーザーが自分自身でトークンを管理し、安全性を確保できることを意味します。
分散型エコシステムにおいて、セキュリティとコントロールの重要性を考慮しての決定です。

### ERC20の特性の継承
FKBTは、**ERC20**トークンのすべての特性を継承しています。
これには、トークンの転送、承認、増加許可、減少許可などの基本的な操作が含まれます。
これにより、FKBTは既存の**ERC20**対応のサービスやdAppsとの連携がスムーズに行えます。

### ユーザーの選択肢の提供
FKBTは、ユーザーにデフォルトの動作モードを選択する権利を与えます。
これは、ユーザーがFKBTを使用する時に、トークンの挙動をカスタマイズできることを意味します。
これにより、ユーザーは自分のニーズに合わせてFKBTを活用できます。

### 広範な実装と採用の促進
FKBTの設計は、広範な実装と採用を促進することを考慮して行われました。
FKBTの導入にあたり、既存の分散型エコシステムに大規模な変更を加える必要がないようにしました。
これにより、FKBTはすぐに採用され、エコシステム全体に適応されることが期待されます。

### allowTransferとallowApprovalの設計
FKBTの開発者とユーザーの両方にとって、`allowTransfer`と`allowApproval`関数は成功した場合に`bool`値を返し、失敗した場合には`revert`するように設計されました。
これは、既存の**ERC20**トークンと同様のパターンを提供し、ユーザーや開発者が予測可能な動作を期待できるようにするためです。

## 後方互換性

KBT（Key-Based Tokens）は、既存のトークン規格やウォレットとの後方互換性を重視して設計されています。
この後方互換性の考え方は、FKBT（Fungible Key-Based Tokens）が導入されても、既存のトークンやウォレットが通常通りに機能し、FKBTの実装による影響を受けないことを示しています。

## テスト

以下にテストコードが置かれています。

https://eips.ethereum.org/assets/eip-6808/test/kbt20.js

平均Gas使用量の表です。
単位は「**GWEI**」です。

| 関数名           | 平均Gas使用量 (GWEI) |
|--------------------|-------------------------|
| addBindings       | 154,991                 |
| resetBindings     | 30,534                  |
| safeFallback      | 51,013                  |
| allowTransfer     | 49,887                  |
| allowApproval     | 44,971                  |

## 参考実装

以下のダイアグラムが参考になります。

https://eips.ethereum.org/assets/eip-6808/Contract%20Interactions%20diagram.svg

以下に参考実装コードが格納されています。

https://github.com/ethereum/EIPs/blob/master/assets/eip-6808/contracts/KBT20.sol

#### `_holderAccounts`

```javascript
mapping(address => AccountHolderBindings) private _holderAccounts;
```

**概要**

アカウントとそのアカウントに関連するバインディング情報を関連付ける配列。

**詳細**

FKBTのアカウントとそれに関連するバインディング情報を保持します。
アカウントごとに `AccountHolderBindings`構造体が関連付けられ、そのアカウントのバインディング情報が格納されます。

---

#### `_firstAccounts`

```javascript
mapping(address => FirstAccountBindings) private _firstAccounts;
```

**概要**

アカウントとそのアカウントに関連するファーストアカウントバインディング情報を関連付ける配列。

**詳細**

アカウントごとに`FirstAccountBindings`構造体が関連付けられ、そのアカウントのファーストアカウントバインディング情報が格納されます。

---

#### `_secondAccounts`

```javascript
mapping(address => SecondAccountBindings) private _secondAccounts;
```

**概要**

アカウントとそのアカウントに関連するセカンドアカウントバインディング情報を関連付ける配列。

**詳細**

アカウントごとに`SecondAccountBindings`構造体が関連付けられ、そのアカウントのセカンドアカウントバインディング情報が格納されます。

---

#### `_transferConditions`

```javascript
mapping(address => TransferConditions) private _transferConditions;
```

**概要**

アカウントとそのアカウントに関連するトランスファーコンディション情報を関連付ける配列。

**詳細**

アカウントごとに`TransferConditions`構造体が関連付けられ、そのアカウントのトランスファーコンディション情報が格納されます。

---

#### `_approvalConditions`

```javascript
mapping(address => ApprovalConditions) private _approvalConditions;
```

**概要**

アカウントとそのアカウントに関連する承認コンディション情報を関連付ける配列。

**詳細**

アカウントごとに`ApprovalConditions`構造体が関連付けられ、そのアカウントの承認コンディション情報が格納されます。

---

#### `_numberOfTransfersAllowed`

```javascript
mapping(address => mapping(address => uint256)) private _numberOfTransfersAllowed;
```

**概要**

アカウントと対応するスペンダーアカウントに許可された送付回数を関連付ける配列。

**詳細**

アカウントごとにスペンダーアカウントとその許可された送付回数が関連付けられます。

**パラメータ**

- `_account`
    - 送付許可情報を持つアカウントのアドレス。
- `_spender`
    - 送付を許可されたスペンダーアカウントのアドレス。

---

#### `addBindings`

```javascript
function addBindings(
    address _keyWallet1,
    address _keyWallet2
) external returns (bool) {
    address sender = _msgSender();
    require(balanceOf(sender) > 0, "[200] KBT20: Wallet is not a holder");
    require(
        _holderAccounts[sender].firstWallet == address(0) &&
            _holderAccounts[sender].secondWallet == address(0),
        "[201] KBT20: Key wallets are already filled"
    );
    require(
        _keyWallet1 != address(0) && _keyWallet2 != address(0),
        "[202] KBT20: Does not follow 0x standard"
    );
    require(
        _keyWallet1 != _keyWallet2,
        "[205] KBT20: Key wallet 1 must be different than key wallet 2"
    );
    require(
        _keyWallet1 != sender,
        "[206] KBT20: Key wallet 1 must be different than the sender"
    );
    require(
        sender != _keyWallet2,
        "[207] KBT20: Key wallet 2 must be different than the sender"
    );
    require(
        _firstAccounts[_keyWallet1].accountHolderWallet == address(0),
        "[203] KBT20: Key wallet 1 is already registered"
    );
    require(
        _secondAccounts[_keyWallet2].accountHolderWallet == address(0),
        "[204] KBT20: Key wallet 2 is already registered"
    );

    _holderAccounts[sender] = AccountHolderBindings({
        firstWallet: _keyWallet1,
        secondWallet: _keyWallet2
    });

    _firstAccounts[_keyWallet1] = FirstAccountBindings({
        accountHolderWallet: sender,
        secondWallet: _keyWallet2
    });

    _secondAccounts[_keyWallet2] = SecondAccountBindings({
        accountHolderWallet: sender,
        firstWallet: _keyWallet1
    });

    emit AccountSecured(sender, balanceOf(sender));

    return true;
}
```

**概要**

FKBTのホルダーにキーウォレットをバインドする関数。

**詳細**

FKBTのホルダーが自身のアカウントに2つのキーウォレットをバインドできるようにします。
バインディングされたキーウォレットはセキュリティ機能の一部として使用されます。
ホルダーは、2つのキーウォレットを指定し、それらが一意であること、かつ送信者のバランスが`0`でないことを確認する必要があります。
また、キーウォレット同士やキーウォレットと送信者の間に重複がないことも確認します。

**引数**

- `_keyWallet1`
  - バインドするファーストキーウォレットのアドレス。
- `_keyWallet2`
  - バインドするセカンドキーウォレットのアドレス。

**戻り値**

- `bool`
  - バインディングが成功した場合は`true`、それ以外の場合は`false`。

---

#### `getBindings`

```javascript
function getBindings(
    address _account
) external view returns (AccountHolderBindings memory) {
    return _holderAccounts[_account];
}
```

**概要**

指定したアカウントに関連付けられたバインディング情報を取得する関数。

**詳細**

この関数は、指定したアカウントに関連付けられたバインディング情報を取得します。
バインディング情報には、ファーストキーウォレットとセカンドキーウォレットのアドレスが含まれています。

**引数**

- ``_account`
  - バインディング情報を取得する対象のアカウントのアドレス。

**戻り値**

- `AccountHolderBindings memory`
  - バインディング情報が格納された`AccountHolderBindings`構造体。

---

#### `resetBindings`

```javascript
function resetBindings() external returns (bool) {
    address accountHolder = _getAccountHolder();
    require(
        accountHolder != address(0),
        "[300] KBT20: Key authorization failure"
    );

    delete _firstAccounts[_holderAccounts[accountHolder].firstWallet];
    delete _secondAccounts[_holderAccounts[accountHolder].secondWallet];
    delete _holderAccounts[accountHolder];

    emit AccountResetBinding(accountHolder);

    return true;
}
```

**概要**

バインディング情報をリセットしてセキュリティ機能を無効にする関数。

**詳細**

バインディング情報を削除し、セキュリティ機能を無効にします。
ホルダーのアカウントに関連付けられたバインディング情報が完全に削除されます。

**戻り値**

- `bool`
  - リセットが成功した場合は`true`、それ以外の場合は`false`。

---

#### `safeFallback`

```javascript
function safeFallback() external returns (bool) {
    address accountHolder = _getAccountHolder();
    address otherSecureWallet = _getOtherSecureWallet();
    require(
        accountHolder != address(0),
        "[400] KBT20: Key authorization failure"
    );

    _transfer(accountHolder, otherSecureWallet, balanceOf(accountHolder));

    emit SafeFallbackActivated(accountHolder);

    return true;
}
```

**概要**

セキュアなフォールバックをアクティブにし、FKBTを別のセキュアなウォレットに転送する関数。

**詳細**

ホルダーのアカウントから別のセキュアなウォレットにFKBTを転送します。
これにより、単一の障害点を防ぎ、セキュリティを向上させます。

**戻り値**

- `bool`
  - アクティブ化が成功した場合は`true`、それ以外の場合は`false`。

---

#### `allowTransfer`

```javascript
function allowTransfer(
    uint256 _amount,
    uint256 _time,
    address _to,
    bool _allFunds
) external virtual returns (bool) {
    address accountHolder = _getAccountHolder();

    require(
        accountHolder != address(0),
        "[500] KBT20: Key authorization failure"
    );
    require(
        balanceOf(accountHolder) >= _amount,
        "[501] KBT20: Not enough tokens"
    );

    _time = _time > 0 ? (block.timestamp + _time) : 0;

    _transferConditions[accountHolder] = TransferConditions({
        amount: _amount,
        time: _time,
        to: _to,
        allFunds: _allFunds
    });

    emit AccountEnabledTransfer(
        accountHolder,
        _amount,
        _time,
        _to,
        _allFunds
    );

    return true;
}
```

**概要**

この関数は、送金条件を設定し、トークンのセキュアな送金を有効にする関数。

**詳細**

ホルダーのアカウントに対して送金条件を設定し、指定された条件での送金を有効にします。
送金条件には、送金額、送金可能な時間、送信先アドレス、すべての資産を送金するかどうかが含まれます。

**引数**

- `_amount`
  - 送金の額。
- `_time`
  - 送金可能な時間を表すブロックタイムスタンプ。
- `_to`
  - トークンを転送する宛先アドレス。
- `_allFunds`
  - すべての資産を送金するかどうかを示すブール値。

**戻り値**

- `bool`
  - 送金条件の設定が成功した場合は`true`、それ以外の場合は`false`。

---

#### `getTransferableFunds`

```javascript
function getTransferableFunds(
    address _account
) external view returns (TransferConditions memory) {
    return _transferConditions[_account];
}
```

**概要**

指定されたアカウントに設定された送金条件を取得する関数。

**詳細**

指定されたアカウントに関連付けられた`transfer`条件を取得します。
`transfer`条件には、送金可能額、送金有効時間、送信先アドレス、すべての資産を送金するかどうかが含まれます。

**引数**

- `_account`
  - 送金条件を取得する対象のアカウントのアドレス。

**戻り値**

- `TransferConditions memory`
  - 送金条件が格納された`TransferConditions`構造体。

---

#### `allowApproval`

```javascript
function allowApproval(
    uint256 _time,
    uint256 _numberOfTransfers
) external virtual override returns (bool) {
    address accountHolder = _getAccountHolder();
    require(
        accountHolder != address(0),
        "[600] KBT20: Key authorization failure"
    );

    _time = block.timestamp + _time;

    _approvalConditions[accountHolder].time = _time;
    _approvalConditions[accountHolder]
        .numberOfTransfers = _numberOfTransfers;

    emit AccountEnabledApproval(accountHolder, _time, _numberOfTransfers);

    return true;
}
```

**概要**

承認条件を設定し、指定された承認条件でトランザクションの承認を有効にする関数。

**詳細**

この関数は、ホルダーのアカウントに対してトランザクションの承認条件を設定し、指定された条件でトランザクションの承認を有効にします。
承認条件には、トランザクションの有効期限のブロックタイムスタンプと許可されるトランザクション数が含まれます。

**引数**

- `_time`
  - トランザクションの有効期限までのブロック数。
- `_numberOfTransfers`
  - 承認されるトランザクションの数。

**戻り値**

- `bool`
  - 承認条件の設定が成功した場合は`true`、それ以外の場合は`false`。

---

#### `getApprovalConditions`

```javascript
function getApprovalConditions(
    address _account
) external view returns (ApprovalConditions memory) {
    return _approvalConditions[_account];
}
```

**概要**

指定されたアカウントに設定されたトランザクション承認条件を取得する関数。

**詳細**

指定されたアカウントに関連付けられたトランザクション承認条件を取得します。
承認条件には、トランザクションの有効期限までのブロック数と許可されるトランザクション数が含まれます。

**引数**

- `_account`
  - トランザクション承認条件を取得する対象のアカウントのアドレス。

**戻り値**

- `ApprovalConditions memory`
  - トランザクション承認条件が格納された`ApprovalConditions`構造体。

---

#### `getNumberOfTransfersAllowed`

```javascript
function getNumberOfTransfersAllowed(
    address _account,
    address _spender
) external view returns (uint256) {
    return _numberOfTransfersAllowed[_account][_spender];
}
```

**概要**

指定されたアカウントから指定された`spebder`に対して許可されるトランザクション数を取得する関数。

**詳細**

指定されたアカウントから指定された`spebder`に対して許可されるトランザクション数を取得します。

**引数**

- `_account`
  - トランザクション数を取得するアカウントのアドレス。
- `_spender`
  - トランザクション数を取得する対象のスプレンダーのアドレス。

**戻り値**

- `uint256`
  - 許可されたトランザクション数。

---

#### `isSecureWallet`

```javascript
function isSecureWallet(address _account) public view returns (bool) {
    return
        _holderAccounts[_account].firstWallet != address(0) &&
        _holderAccounts[_account].secondWallet != address(0);
}
```

**概要**

指定されたアカウントがセキュアなウォレットかどうかを判定する関数。

**詳細**

指定されたアカウントがセキュアなウォレットであるかどうかを判定します。
セキュアなウォレットは、バインディングされたファーストキーウォレットとセカンドキーウォレットを持つアカウントです。

**引数**

- `_account`
  - セキュアなウォレットかどうかを判定するアカウントのアドレス。

**戻り値**

- `bool`
  - 指定されたアカウントがセキュアなウォレットである場合は`true`、それ以外の場合は`false`。

---


#### `transfer`

```javascript
function transfer(
    address _to,
    uint256 _amount
) public virtual override returns (bool) {
    address _owner = _msgSender();

    if (isSecureWallet(_owner)) {
        require(
            _hasAllowedTransfer(_owner, _amount, _to),
            "[100] KBT20: Sender is a secure wallet and doesn't have approval for the amount"
        );
    }

    _transfer(_owner, _to, _amount);

    delete _transferConditions[_owner];

    return true;
}
```

**概要**

トークンを指定されたアドレスに送信する関数。

**詳細**

呼び出し元のアカウントから指定されたアドレスに指定された数量のトークンを送信します。
セキュアなウォレットからのトークン送信の場合、送信元が指定数量を承認していることを確認します。

**引数**

- `_to`
  - トークンを送信する対象のアドレス。
- `_amount`
  - 送信するトークンの数量。

**戻り値**

- `bool`
  - トークンの送信が成功した場合は`true`、それ以外の場合は`false`。

---

#### `approve`

```javascript
function approve(
    address _spender,
    uint256 _amount
) public virtual override returns (bool) {
    address _owner = _msgSender();
    if (isSecureWallet(_owner)) {
        require(
            _approvalConditions[_owner].time > 0,
            "[101] KBT20: Spending of funds is not authorized."
        );
        require(
            _isApprovalAllowed(_owner),
            "[102] KBT20: Time has expired for the spending of funds"
        );
    }

    _approve(_owner, _spender, _amount);

    _numberOfTransfersAllowed[_owner][_spender] = _approvalConditions[
        _owner
    ].numberOfTransfers;

    delete _approvalConditions[_owner];

    return true;
}
```

**概要**

指定されたアドレスが自身のトークンを一定数量だけ支出できるように承認する関数。

**詳細**

呼び出し元のアカウントが指定されたアドレス（スプレンダー）が一定数量のトークンを送金できるように承認します。
セキュアなウォレットからのトークン承認の場合、承認条件が満たされていることを確認します。

**引数**

- `_spender`
  - トークンを支出できるようにするアドレス。
- `_amount`
  - `spender`に対して承認するトークンの数量。

**戻り値**

- `bool`
  - 承認が成功した場合は`true`、それ以外の場合は`false`。

---

#### `transferFrom`

```javascript
function transferFrom(
    address _from,
    address _to,
    uint256 _amount
) public virtual override returns (bool) {
    address spender = _msgSender();
    _spendAllowance(_from, spender, _amount);
    _transfer(_from, _to, _amount);

    if (_numberOfTransfersAllowed[_from][spender] != 0) {
        if (_numberOfTransfersAllowed[_from][spender] == 1) {
            _approve(_from, spender, 0);
        }
        _numberOfTransfersAllowed[_from][spender] -= 1;
    }
    return true;
}
```

**概要**

指定されたアドレスから別のアドレスにトークンを送付する関数。

**詳細**

指定されたアドレスから別のアドレスに指定された数量のトークンを送付します。
セキュアなウォレットからのトークン送信の場合、送信元がトークン承認条件を満たしていることを確認します。

**引数**

- `_from`
  - トークンを送信するアドレス。
- `_to`
  - トークンを送信する対象のアドレス。
- `_amount`
  - 送信するトークンの数量。

**戻り値**

- `bool`
  - トークンの送信が成功した場合は`true`、それ以外の場合は`false`。

---

#### `increaseAllowance`

```javascript
function increaseAllowance(
    address _spender,
    uint256 _addedValue
) public virtual override returns (bool) {
    address _owner = _msgSender();
    require(
        _approvalConditions[_owner].time > 0,
        "[101] KBT20: Spending of funds is not authorized."
    );
    require(
        _isApprovalAllowed(_owner),
        "[102] KBT20: Time has expired for the spending of funds"
    );

    _approve(_owner, _spender, allowance(_owner, _spender) + _addedValue);

    delete _approvalConditions[_owner];

    return true;
}
```

**概要**

`spender`がトークンを支出できる額を増やす関数。

**詳細**

指定されたアドレス（`spender`）が送付できるトークンの数量を増やします。
セキュアなウォレットからのトークン承認条件が満たされていることを確認します。

**引数**

- `_spender`
  - トークンを支出できるアドレス（`spender`）。
- `_addedValue`
  - `spender`に対して追加で承認するトークンの数量。

**戻り値**

- `bool`
  - トークンの承認が成功した場合は`true`、それ以外の場合は`false`。

---

#### `decreaseAllowance`

```javascript
function decreaseAllowance(
    address _spender,
    uint256 _subtractedValue
) public virtual override returns (bool) {
    address _owner = _msgSender();
    require(
        _approvalConditions[_owner].time > 0,
        "[101] KBT20: Spending of funds is not authorized."
    );
    require(
        _isApprovalAllowed(_owner),
        "[102] KBT20: Time has expired for the spending of funds"
    );

    uint256 currentAllowance = allowance(_owner, _spender);
    require(
        currentAllowance >= _subtractedValue,
        "ERC20: decreased allowance below zero"
    );
    unchecked {
        _approve(_owner, _spender, currentAllowance - _subtractedValue);
    }

    delete _approvalConditions[_owner];

    return true;
}
```

**概要**

`spender`がトークンを支出できる額を減らす関数。

**詳細**

指定されたアドレス（`spender`）が送付できるトークンの数量を減らします。
セキュアなウォレットからのトークン承認条件が満たされていることを確認します。

**引数**

- `_spender`
  - トークンを支出できるアドレス（`spender`）。
- `_subtractedValue`
  - `spender`から差し引くトークンの数量。

**戻り値**

- `bool`
  - トークンの承認が成功した場合は`true`、それ以外の場合は`false`。

---

#### `_afterTokenTransfer`

```javascript
function _afterTokenTransfer(
    address from,
    address to,
    uint256 amount
) internal virtual override {
    // region update secureAccounts
    if (isSecureWallet(from) && balanceOf(from) == 0) {
        delete _firstAccounts[_holderAccounts[from].firstWallet];
        delete _secondAccounts[_holderAccounts[from].secondWallet];
        delete _holderAccounts[from];
    }
    // endregion

    if (balanceOf(from) == 0) {
        emit Egress(from, amount);
    }

    if (balanceOf(to) == amount) {
        emit Ingress(to, amount);
    }
}
```

**概要**

トークンの送信後に実行される処理を管理する関数。

**詳細**

トークンの送信が完了した後に特定のアクションを実行します。
送信元がセキュアなウォレットで、そのウォレットのトークン残高が`0`になった場合、関連するウォレットのバインディング情報を削除します。
また、送信元の残高が`0`になった場合、`Egress`イベントを発行し、送信先の残高が指定数量のトークンになった場合、`Ingress`イベントを発行します。

**引数**

- `from`
  - トークンの送信元アドレス。
- `to`
  - トークンの送信先アドレス。
- `amount`
  - 送信されたトークンの数量。

---

#### `_hasAllowedTransfer`

```javascript
function _hasAllowedTransfer(
    address _account,
    uint256 _amount,
    address _to
) internal view returns (bool) {
    TransferConditions memory conditions = _transferConditions[_account];

    if (conditions.allFunds) {
        return true;
    }

    if (
        (conditions.amount == 0 &&
            conditions.time == 0 &&
            conditions.to == address(0)) ||
        (conditions.amount > 0 && conditions.amount < _amount) ||
        (conditions.time > 0 && conditions.time < block.timestamp) ||
        (conditions.to != address(0) && conditions.to != _to)
    ) {
        return false;
    }

    return true;
}
```

**概要**

トークン送信が許可されているかどうかを判定する関数。

**詳細**

指定されたアカウントが指定された条件下でトークン送信を許可しているかどうかを判定します。
特定の条件（数量、時間、送信先など）が設定されている場合、これらの条件を確認し、許可されているかどうかを判定します。

**引数**

- `_account`
  - トークン送信元のアカウントアドレス。
- `_amount`
  - 送信しようとしているトークンの数量。
- `_to`
  - トークンの送信先アドレス。

**戻り値**

- `bool`
  - トークン送信が許可されている場合は`true`、それ以外の場合は`false`。

---

#### `_isApprovalAllowed`

```javascript
function _isApprovalAllowed(address account) internal view returns (bool) {
    return _approvalConditions[account].time >= block.timestamp;
}
```

**概要**

トークン支出が許可されているか判定する関数。

**詳細**

指定されたアカウントのトークン送付が許可されているかどうかを判定します。
送付が許可されている場合、時間制限が満たされているかどうかを確認します。

**引数**

- `account`
  - トークン支出を行おうとしているアカウントのアドレス。

**戻り値**

- `bool`
  - トークン支出が許可されている場合は`true`、それ以外の場合は`false`。

---

#### `_getAccountHolder`

```javascript
function _getAccountHolder() internal view returns (address) {
    address sender = _msgSender();
    return
        _firstAccounts[sender].accountHolderWallet != address(0)
            ? _firstAccounts[sender].accountHolderWallet
            : (
                _secondAccounts[sender].accountHolderWallet != address(0)
                    ? _secondAccounts[sender].accountHolderWallet
                    : address(0)
            );
}
```

**概要**

アカウントのアカウントホルダーを取得する関数。

**詳細**

呼び出し元のアカウントのアカウントホルダーを取得します。
アカウントホルダーは、複数のウォレット間でのトークンの移動を管理するために使用されます。
ウォレットが複数のアカウントホルダーに関連付けられている場合、適切なアカウントホルダーを特定します。

**戻り値**

- `address`
  - アカウントホルダーのアドレス。存在しない場合は`address(0)`。

---

#### `_getOtherSecureWallet`

```javascript
function _getOtherSecureWallet() internal view returns (address) {
    address sender = _msgSender();
    address accountHolder = _getAccountHolder();

    return
        _holderAccounts[accountHolder].firstWallet == sender
            ? _holderAccounts[accountHolder].secondWallet
            : _holderAccounts[accountHolder].firstWallet;
}
```

**概要**

セキュアなウォレットのうち、もう一方のウォレットを取得する関数。

**詳細**

呼び出し元のアカウントが関連付けられているセキュアなウォレットのうち、もう一方のウォレットを取得します。
セキュアなウォレットは、セキュアなトランザクションの一部として使用されます。

**戻り値**

- `address`
  - セキュアなウォレットのうち、もう一方のウォレットのアドレス。
  - 存在しない場合は`address(0)`。


## セキュリティ考慮事項

**キーウォレット**
- `addBindings`関数を呼び出す時、ユーザーは2つのウォレットを指定する必要があります。
- これらのウォレットは`_keyWallet1`と`_keyWallet2`として機能し、同時に追加されます。
- これにより、ユーザーが負担する料金を削減し、人為的なエラーを減らし、問題を未然に防ぎます。
- 複数のウォレットを追加することは、`safeFallback`の問題を防ぐためです。
- セキュリティ機能が有効な場合、FKBTは3つのウォレットシステムで動作します。

**safeFallback**
- 所有者が保有ウォレットが漏洩したと考える場合や、アクセスを失った場合に使用されます。
- ユーザーは`_keyWallet1`から`safeFallback`を呼び出すことができ、FKBTは保有ウォレットから別のキーウォレットにリダイレクトされます。
- これにより、攻撃者がFKBTを全てアクセスできるリスクが減少します。

**resetBindings**
- 所有者が`_keyWallet1`や`_keyWallet2`が漏洩したと考える場合や、アクセスを失った場合に使用されます。
- ユーザーは`resetBindings`を呼び出すことで、バインドされたキーウォレットを削除し、セキュリティ機能をリセットできます。
- FKBTは、新しいキーウォレットが再び追加されるまで通常の**ERC20**として動作します。

**allowTransfer**
- 安全なトランスファーを保証し、dAppが通常の**ERC20**のデフォルト動作を実行できるようにします。
- ユーザーはトランスファーの詳細を指定し、FKBTを無制限にアンロックできます。
- セキュリティはユーザーに委ねられます。

- この関数には4つのパラメータがあり、組み合わせによって異なるセキュリティレベルが設定できます。
  - `_amount8`
      - トランスファーに使用されるFKBTの数量。
  - `_time9`
      - トランスファーの有効期限（ブロック数）。
  - `_address10`
      - トランスファーの宛先アドレス。
  - `_allFunds11`
      - ブール値で、`true`の場合は通常の**ERC20**のデフォルト動作に戻ります。

**allowApproval**
- サードパーティがユーザーのFKBTを使用する時の追加のセキュリティを提供します。
- 特に悪意ある攻撃に対抗するために有用です。
- この関数には2つのパラメータがあり、組み合わせによって異なるセキュリティレベルが設定できます。
  - `_time12`
      - サードパーティの承認の有効期限（ブロック数）。
  - `_numberOfTransfers13`
      - サードパーティがユーザーの代わりに実行できるトランザクション数。

これらの設計決定により、FKBTはセキュリティを重視し、様々なセキュリティレベルをユーザーに提供します。


## 引用

Mihai Onila (@MihaiORO), Nick Zeman (@NickZCZ), Narcis Cotaie (@NarcisCRO), "ERC-6808: Fungible Key Bound Token [DRAFT]," Ethereum Improvement Proposals, no. 6808, March 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-6808.

## 最後に

今回は「**FKBT**と呼ばれる**Fungible Key Bound Tokens**という、**ERC20**トークンの送付をより安全に実行する規格を提案している**ERC6808**」についてまとめてきました！
いかがだったでしょうか？

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

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

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

https://chaldene.net/

https://zenn.dev/heku

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58
