[ERC6065] 不動産NFTのインターフェースの仕組みを理解しよう！

# はじめに

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

https://cryptogames.co.jp/

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

https://cryptospells.jp/

今回は、**ERC721**規格を拡張して不動産NFTを管理するインターフェースを提案している**ERC6065**についてまとめていきます！

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

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

:::note warn
**6065**は現在（2023年9月10日）では「**Review**」段階です。
:::

# 概要

この提案は、ブロックチェーン上で不動産や物件を表現するための構造を提供しています。
これにより、不動産をデジタルトークンとして扱うことが可能になります。
これは**ERC721**という規格を基にしており、不動産のような実世界の資産をブロックチェーン上で管理するために必要な特別な機能を追加しています。

この提案の主な目標は、次の3つです。

1. **NFTの普遍的な譲渡可能性**
NFT（非代替可能トークン）を簡単に他の人に譲渡できることを意味します。
つまり、不動産を持つ人が、それを他の人に売ったり贈ったりすることができる仕組みを提供します。

2. **NFTに関連付けられた私的財産権**
NFTが特定の所有者に関連付けられた財産権を持つことを意味します。
例えば、不動産をNFTとして所有した場合、そのNFTは所有者に対する法的権利や所有権を表現します。

3. **NFTの譲渡と財産権の原子的な譲渡**
NFTを転送すると同時に、そのNFTに関連する財産権も一緒に譲渡されることを指します。
つまり、不動産を譲渡すると、それに関連する権利や契約も一緒に譲渡されます。

このトークンには、不動産に関する詳細情報も含まれています。
具体的には、運営契約の詳細（法的なルール）、物件の識別情報、債務額や差し押さえの状態、そして物件を管理する人のアドレスなどが含まれています。
これにより、不動産取引をブロックチェーン上で透明かつ効率的に行うことができるようになります。

# 動機

不動産は世界で最も価値のある資産クラスの1つです。
しかし、不動産取引は通常、多くの手続きとコストがかかり、情報が非対称的で、所有権の変更が煩雑です。
この提案は、不動産をデジタルトークンとしてブロックチェーン上に表現するアイデアです。
これにより、不動産取引の参入障壁が低くなり、取引のコストが削減され、情報の透明性が高まり、所有権の形態が柔軟になり、新しいイノベーションが生まれる可能性があります。

ただし、不動産は現実世界に存在し、ブロックチェーン上でのトークンはデジタルです。
これらの2つは異なるルールで動作します。
ブロックチェーン上のトークンは、分散型のネットワークで合意形成され、暗号学的な保証で所有権が証明されます。
一方、不動産は法的契約や法律、裁判所の判決によって所有権が確立されます。

この提案は、現実世界の不動産をブロックチェーン上でシームレスに取り扱えるようにするための標準を提供しようとしています。
つまり、不動産NFT（トークン）をデジタルNFTと同じようにブロックチェーン上でスムーズに操作できるようにすることを目指しています。

# 仕様

この提案では、不動産をブロックチェーン上で所有権を表現するためのトークン構造を紹介しています。
このトークンは、広く使用されている**ERC721**というNFT（非代替可能トークン）の標準を基にしています。
このトークンは、不動産の所有権や関連情報を効率的に管理するために以下の要素を含んでいます。

## トークンコンポーネント

1. **ERC721の継承**
このトークンは既存のNFT標準と互換性があります。
これは、他のNFTと同様に取り扱えることを意味します。

2. **operatingAgreementHashOf**
不動産に関する法的契約の詳細を表す不変のハッシュです。
不動産の所有権や利用条件などの法的情報がトークンに関連付けられます。

3. **Property Unique Identifiers**
不動産を一意に識別するための情報が含まれます。
物理的な権利書に記載されている法的説明、実際の住所、GIS座標、地番/税金ID、法的所有エンティティ（権利書に記載されている情報）が含まれます。
これにより、どの不動産に関するトークンかを特定できます。

4. **debtOf**
トークンに関連する債務情報が含まれます。
具体的な債務額、通貨、および差し押さえの状態が記録され、トークンの透明性が向上します。

5. **managerOf**
不動産を管理するためのEthereumアドレスが含まれます。
これにより、不動産の管理者が誰であるかがトークン上で明確に示されます。

このトークン構造により、不動産のデジタル化が実現され、不動産の所有権や関連情報がブロックチェーン上で効率的に取り扱えるようになります。

## インターフェース

この提案（EIP）は、不動産をブロックチェーン上でトークン化するための標準規格である**ERC721**を基にしています。
**ERC721**は、非代替可能トークン（NFT）を作成するためによく使用されている規格です
この提案では、**ERC721**のトークン転送と承認に関するルールをそのまま継承しています。
つまり、不動産トークンを他の人に譲渡したり、承認したりするための基本的な方法は変更されず、**ERC721**の規格に従います。

さらに、この提案は**ERC721 Metadata**規格も継承しています。
これにより、不動産トークンに関する名前、シンボル、およびメタデータURI（リソースの場所を示す情報）を取得するための標準が提供されます。
この機能により、不動産トークンは既存のNFT取引所やサービスと連携しやすくなります。
ただし、連携する際に注意が必要な点も存在します。

```javascript
pragma solidity ^0.8.13;

import "forge-std/interfaces/IERC721.sol";

interface IERC6065 is IERC721 {

	// This event MUST emit if the asset is ever foreclosed.
	event Foreclosed(uint256 id);

	/* 
	Next getter functions return immutable data for NFT.
	*/
	function legalDescriptionOf(uint256 _id) external view returns (string memory);
	function addressOf(uint256 _id) external view returns (string memory);
	function geoJsonOf(uint256 _id) external view returns (string memory);
	function parcelIdOf(uint256 _id) external view returns (string memory);
	function legalOwnerOf(uint256 _id) external view returns (string memory);
	function operatingAgreementHashOf(uint256 _id) external view returns (bytes32);

	/*
	Next getter function returns the debt denomination token of the NFT, the amount of debt (negative debt == credit), and if the underlying 
	asset backing the NFT has been foreclosed on. This should be utilized specifically for off-chain debt and required payments on the RWA asset.
	It's recommended that administrators only use a single token type to denominate the debt. It's unrealistic to require integrating smart
	contracts to implement possibly unbounded tokens denominating the off-chain debt of an asset.

	If the foreclosed status == true, then the RWA can be seen as severed from the NFT. The NFT is now "unbacked" by the RWA.
	*/
	function debtOf(uint256 _id) external view returns (address debtToken, int256 debtAmt, bool foreclosed);

	// Get the managerOf an NFT. The manager can have additional rights to the NFT or RWA on or off-chain.
	function managerOf(uint256 _id) external view returns (address);
}
```

# 補足

## 紹介

現実世界の資産は複雑で予測困難な状況で運用されます。
そのため、資産の実際の状態を確認することは、曖昧でコストが高いか時間がかかることがあります。
例えば、アメリカでは不動産の所有権変更は通常、郡の登記官のオフィスで行われ、時には手書きで記録されます。
ブロックチェーン上でNFTの取引が行われるたびに、この手作業の記録を更新するのは実現不可能です。
さらに、現実世界の不動産権利は法廷によって実施されるため、必要に応じて裁判所が所有権を解釈し、実行できるように不動産の所有権が文書化されることが重要です。

そのため、ブロックチェーン上の不動産NFTの状態が物理的な対応物と正確に一致することを確認する責任を負う信頼性のある第三者が必要です。
不動産の管理者が、物理的な不動産に関する法的に拘束力のあるデジタル表現を発行する役割を果たすことで、NFTの譲渡と不動産権利の同時譲渡を実現し、不動産の所有権に関連する支払いや届出などの手続きをスムーズに行うことができます。
これは、NFTが譲渡されるたびに法的な所有権の変更を行わずに済むことを可能にします。アメリカでの不動産トークン化のために提案された管理者の法的構造の例が、参考実装として提供されています。
ただし、この標準を実装するトークンが必ずしも同様の法的構造を持つ必要はありません。

## ガイドの目的

このEIPは、物理的な不動産のNFT（非代替可能トークン）表現を作成するための3つの主要な目標を達成するために設計されています。

1. **不動産NFTは普遍的に譲渡可能であること**
私有財産としての不動産は、所有権を持つ法的な個人または法人に自由に譲渡できることが重要です。
そのため、物理的な不動産を表すNFTは、誰にでも所有権を譲渡できる普遍的な自由を維持する必要があります。

2. **不動産所有権に関連するすべての権利はNFTによって維持および保証できること**
不動産の所有権には、財産を所有し、占有し、賃貸し、変更し、再販し、譲渡する権利が含まれます。
これらの権利は、NFTの表現によっても維持および保証できる必要があります。
つまり、NFTを持つことで、これらの所有権が確保され、必要に応じて強制できるようになります。

3. **財産権はNFTの譲渡と同時に原子的に移転されること**
ブロックチェーン上のトークンの所有権は、デジタルトークンの譲渡と同時に移転されます。
物理的な不動産のデジタル表現がブロックチェーン技術の利点を最大限に活用するために、財産に関連する権利もデジタルトークンの譲渡と同時に1つの処理の中で移転される必要があります。
これにより、不動産の取引がスムーズに行われ、所有権の変更が効果的に管理されます。

## オペレーティング・アグリーメント・ハッシュ・オブ

このNFTには、特定の不動産に関する法的文書の情報を表す「**不変のハッシュ（固定された一意の識別子）**」が含まれています。
この法的文書は、実際の不動産に関する権利、条件、規定などが記載されています。
そして、この文書のハッシュは変更不可（不変）である必要があります。
なぜなら、将来、この文書に基づく権利や条件が、NFTを受け取った人や他の関係者にとっても法的に有効で実行可能であることを確保するためです。

NFTが譲渡されると、新しい所有者は即座にこの法的文書に基づく権利を行使できます。
不動産の法的構造や権利、条件に変更が生じる場合、元のNFTは無効とし、新しいハッシュを持つ新しいNFTを発行する必要があります。
この新しいNFTは、変更後の法的文書に基づくものとなります。

## プロパティの固有識別子

NFTには、不動産を一意に識別するための重要な情報が含まれています。
これらの情報は変更できないようになっていて、次のようなものです。

1. **legalDescriptionOf（物件の法的説明）**
不動産権利書から抜粋された、物件の正確な法的説明が含まれています。
これにより、不動産の特徴が文書に基づいて確実に識別できます。

2. **addressOf（物件の住所）**
物件の実際の住所が含まれていて、物理的な場所が特定できます。

3. **geoJsonOf（物件のGeoJSON）**:
動産の地理空間座標がGeoJSON形式で提供されていて、地図上で不動産の位置を示す情報が含まれています。

4. **parcelIdOf（物件のID番号）**
地方自治体が使用する、不動産を一意に識別するためのID番号が含まれています。

5. **legalOwnerOf（法的所有者）**
不動産の正式な所有者である法的実体（個人または団体）が示され、所有者が確認できます。

これらの一意の識別子は、不動産を識別し、その正当性を保証するために不変（変更不可）である必要があります。
これは、将来的に不動産に関する紛争が生じた場合に、NFTの所有者にとって信頼性のある情報源となります。
特に`legalOwnerOf`（法的所有者）は、所有権と契約の正当性をオフチェーンで確認するために重要であり、将来的には`Chainlink`などのサービスを通じて簡単に確認できるようになる可能性があります。

## debtOf

この情報は、不動産に関連する債務とそれに対応する通貨の値を示します。
正の残高は、不動産に対する未払いの債務を表し、負の残高はNFTの所有者が請求できるクレジットを示します。
これにより、不動産の管理者はNFTの所有者に対して、不動産税や重要な修理・保守など、実際の不動産に関連する支払いを要求できます。
同様に、管理者とNFTの所有者が不動産の管理契約や収益共有契約を取り決めている場合、クレジットがNFTの所有者に付与されることもあります。

また、`debtOf`（債務情報を提供する）関数は、NFTによって表される資産の差し押さえ状態も示します。
`true`の場合、関連する不動産はNFTをサポートしていないことを意味し、`false`の場合、関連する不動産はまだNFTをサポートしていることを示します。
管理者は、**Operating Agreement**で指定された理由に基づいて、資産を差し押さえることができます。
スマートコントラクトは、この関数を呼び出すことで差し押さえの状態を確認できます。
資産が差し押さえられた場合、NFTをサポートする**RWA**が削除されたことを理解する必要があります。
そのため、スマートコントラクトは評価やその他の計算を行う際にこれを考慮する必要があります。

これらの値の更新方法については特定の標準要件は存在しません。
詳細は実装者によって決定されますが、このEIPではこれらの値の表現方法と読み取り方が標準化され、統合が容易になるようになっています。

## managerOf

この機能は、NFT（非代替可能トークン）の所有権と管理権を分離できる仕組みを提供します。
具体的には、NFTを発行したEthereumアドレスが実際の所有者である一方で、他のEthereumアドレスに特定の権限を付与できるようにします。

例えば、不動産のNFTを所有しているのは貸し出しプロトコルのスマートコントラクトである場合、実際の不動産の所有者ではありませんが、このスマートコントラクトが特定の操作を実行する権限を持つ別のEthereumアドレス（たとえば、預金者）に与えることができます。
これにより、プロトコルやスマートコントラクトが不動産NFTに関連するアクションを実行でき、柔軟性が向上します。

この機能により、実際の所有者と操作権限を分離し、異なるアドレスに異なる役割と権限を割り当てることができるため、不動産NFTの利用範囲が拡大し、さまざまなシナリオに対応できるようになります。

# 後方互換性

このEIPは、**ERC721**というNFTの標準に基づいていますが、新しい機能や仕組みが導入されています。
つまり、**ERC721**と同じように動作することができる一方で、より多くの機能を提供します。
しかし、これらの新機能を実際に利用する前に、スマートコントラクトの統合に際して考慮すべき事項があります。

:::note info
このEIPを使用する際には、スマートコントラクトの設計や実装に関するセキュリティや互換性の問題に留意する必要があります。
新しい機能が追加されたため、それらを適切に理解し、統合する際には注意が必要です。
また、セキュリティの観点からも検討事項が存在します。
スマートコントラクトの脆弱性やセキュリティのリスクを最小限に抑えるために、十分な注意が必要です。
:::

# 参考実装

以下のGithubに格納しています。

https://github.com/cardene777/base-code/blob/develop/contract/contracts/ERC6065/Implementation.sol

引用: https://eips.ethereum.org/EIPS/eip-6065

Klasma Labsは進行中のリファレンス実装を提供しています。
この技術的な実装には、以下の追加コンポーネントが含まれており、これらは参考のために提供されていますが、必ずしも採用する必要はありません。

## 実装の要約

- **NFTの焼却と生成機能**
    - NFTを無効化（焼却）し、新しいNFTを生成する機能が含まれています。
- **不変のNFTデータ（一意の識別子と運用契約のハッシュ）**
    - NFTに関連する重要なデータ、特に一意の識別子と契約のハッシュ値は、変更不可（不変）で格納されます。
    - これにより、NFTのデータが保護され、変更されないことが保証されます。
- **管理者による簡単な債務追跡**
    - 管理者がNFT所有者に対して債務を監視し、トラッキングできる仕組みが提供されています。
- **不正なアドレスが保有する資産を凍結するためのブロックリスト機能（注：将来的に実装予定）**
    - 将来の実装で追加される予定の機能で、不正なアドレスが資産を凍結できるようにします。
- **管理者によって開始される単純な差し押さえロジック**
    - 管理者が不動産資産を差し押さえるための簡単なロジックが実装されています。
- **他のサポートされるスマートコントラクトにこの呼び出しをチェーンする`managerOf`関数の実装**
    - この関数は、他のスマートコントラクトに呼び出しを連鎖させるための実装です。

## 実装の解説

### ADMINISTRATOR

```javascript
address public ADMINISTRATOR;
```

**概要**
トークンの管理者のアドレス。

---

### COUNTER

```javascript
uint256 public COUNTER;
```

**概要**
一意のトークンを識別する値。

---

### URIBASE

```javascript
string public URIBASE;
```

**概要**
RI（Uniform Resource Identifier）のベース。
トークンのメタデータにアクセスするために使用されます。

---

### EIP6065Immutable

```javascript
struct EIP6065Immutable {
    string legal_description_of_property;
    string street_address;
    string geo_json;
    string parcel_id;
    string legal_owner;
    bytes32 operating_agreement_hash;
}
```

**概要**
不動産メタデータを格納する構造体。

**パラメータ**
- `legal_description_of_property`
    - 不動産の法的説明を示す文字列。
- `street_address`
    - 不動産の住所を示す文字列。
- `geo_json`
    - 不動産のジGeoJSONデータを示す文字列。
- `parcel_id`
    - 不動産のパーセルIDを示す文字列。
- `legal_owner`
    - 不動産の法的所有者を示す文字列。
- `operating_agreement_hash`
    - 運用契約のハッシュ値。

---

### EIP6065Mutable

```javascript
struct EIP6065Mutable {
    address debt_token;
    int256 debt_amt;
    bool foreclosed;
}
```

**概要**
変更可能なトークンメタデータを格納する構造体。

**パラメータ**
- `debt_token`
    - 債務トークンのアドレス。
- `debt_amt`
    - 債務額を示す整数値。
- `foreclosed`
    - 差し押さえ状態を示すブール値。

---

### DebtTokenChanged

```javascript
event DebtTokenChanged(uint256 id, address newToken);
```

**概要**
債務トークンが変更された時に発行されるイベント。

**パラメータ**
- `id`
    - 変更されたトークンのトークンID。
- `newToken`
    - 新しい債務トークンのアドレス。

---

### DebtBalanceChanged

```javascript
event DebtBalanceChanged(uint256 id, int256 changedBy, int256 newAmt);
```

**概要**
債務の残高が変更された時に発行されるイベント。

**パラメータ**
- `id`
    - 変更されたトークンのトークンID。
- `changedBy`
    - 残高の変更量を示す整数値。
- `newAmt`
    - 新しい残高を示す整数値。

---

### DebtPaid

```javascript
event DebtPaid(uint256 id, uint256 paidAmt, int256 remainingAmt);
```

**概要**
債務が支払われた時に発行されるイベント。

**パラメータ**
- `id`
    - 対象トークンのトークンID。
- `paidAmt`
    - 支払われた金額を示す整数値。
- `remainingAmt`
    - 残りの債務を示す整数値。

---

### CreditClaimed

```javascript
event CreditClaimed(uint256 id, uint256 claimedAmt);
```

**概要**
クレジットが請求された時に発行されるイベント。

**パラメータ**
- `id`
    - 請求されたトークンのトークンID。
- `claimedAmt`
    - 請求されたクレジットの金額を示す整数値。

---

### Foreclosed

```javascript
event Foreclosed(uint256 id);
```

**概要**
差し押さえが実行された時に発行されるイベント。

**パラメータ**
- `id`
    - 差し押さえられたトークンのトークンID。

---

### setAdmin

```javascript
function setAdmin(address _new) external onlyAdmin {
    ADMINISTRATOR = _new;
}
```

**概要**
新しい管理者アドレスを設定する関数。

**引数**
- `_new`
    - 新しい管理者のアドレス。

---

### setUriBase

```javascript
function setUriBase(string calldata _new) external onlyAdmin {
    URIBASE = _new;
}
```

**概要**
新しいURIベースを設定する関数。

**引数**
- `_new`
    - 新しいURIベースを示す文字列。

---

### tokenURI

```javascript
function tokenURI(uint256 _id) public view override tokenExists(_id) returns (string memory){
    return string(abi.encodePacked(URIBASE, _id.toString()));
}
```

**概要**
指定されたトークンのURIを生成して返す関数。

**詳細**
URIは`URIBASE`とトークンのIDを結合して生成されます。

**引数**
- `_id`
    - トークンのID。

**戻り値**
- トークンのURIを示す文字列。

---

### legalDescriptionOf

```javascript
function legalDescriptionOf(uint256 _id) external view tokenExists(_id) returns (string memory){
    return EIP6065ImmutableMetadata[_id].legal_description_of_property;
}
```

**概要**
指定されたトークンの法的説明を取得する関数。

**引数**
- `_id`
    - トークンのID。

**戻り値**
- トークンの法的説明を示す文字列。

---

### addressOf

```javascript
function addressOf(uint256 _id) external view tokenExists(_id) returns (string memory){
    return EIP6065ImmutableMetadata[_id].street_address;
}
```

**概要**
指定されたトークンの住所を取得する関数。

**引数**
- `_id`
    - トークンのID。

**戻り値**
- トークンの住所を示す文字列。

---

### geoJsonOf

```javascript
function geoJsonOf(uint256 _id) external view tokenExists(_id) returns (string memory){
    return EIP6065ImmutableMetadata[_id].geo_json;
}
```

**概要**
指定されたトークンのGeoJSONデータを取得する関数。

**引数**
- `_id`
    - トークンのID。

**戻り値**
- トークンのジオJSONデータを示す文字列。

---

### parcelIdOf

```javascript
function parcelIdOf(uint256 _id) external view tokenExists(_id) returns (string memory){
    return EIP6065ImmutableMetadata[_id].parcel_id;
}
```

**概要**
指定されたトークンのパーセルIDを取得する関数。

**引数**
- `_id`
    - トークンのID。

**戻り値**
- トークンのパーセルIDを示す文字列。

---

### legalOwnerOf

```javascript
function legalOwnerOf(uint256 _id) external view tokenExists(_id) returns (string memory){
    return EIP6065ImmutableMetadata[_id].legal_owner;
}
```

**概要**
指定されたトークンの法的所有者を取得する関数。

**引数**
- `_id`
    - トークンのID。

**戻り値**
- トークンの法的所有者を示す文字列。

---

### operatingAgreementHashOf

```javascript
function operatingAgreementHashOf(uint256 _id) external view tokenExists(_id) returns (bytes32){
    return EIP6065ImmutableMetadata[_id].operating_agreement_hash;
}
```

**概要**
指定されたトークンの運用契約のハッシュを取得する関数。

**引数**
- `_id`
    - トークンのID。

**戻り値**
- トークンの運用契約のハッシュを示すバイト32。

---

### debtOf

```javascript
function debtOf(uint256 _id) external view tokenExists(_id) returns (address, int256, bool){
    EIP6065Mutable memory _data = EIP6065MutableMetadata[_id];
    return (_data.debt_token, _data.debt_amt, _data.foreclosed);
}
```

**概要**
指定されたトークンの債務情報を取得する関数。

**詳細**
- 戻り値は債務トークンのアドレス、債務額、および差し押さえの状態を含みます。

**引数**
- `_id`
    - トークンのID。

**戻り値**
- 債務トークンのアドレス、債務額（整数）、差し押さえの状態（真偽値）。

---

### managerOf

```javascript
function managerOf(uint256 _id) external view returns (address){ // note: modifier removed, implement in code for efficiency tokenExists(_id)
    address _owner = ownerOf(_id);
    require(_owner != address(0), "NOT_MINTED");

    uint256 _codeSize;
    assembly {
        _codeSize := extcodesize(_owner)
    }
    if (_codeSize == 0){
        return _owner;
    }
    else {
        try IERC165(_owner).supportsInterface(0x01ffc9a7) returns (bool _check1) { // 0x01ffc9a7 is EIP165 interface
            if (_check1){
                try IERC165(_owner).supportsInterface(0xffffffff) returns (bool _check2){ // 0xffffffff is required false check, see: https://eips.ethereum.org/EIPS/eip-165
                    if (!_check2){
                        bool _check3 = IERC165(_owner).supportsInterface(0x9325945c); // 0x9325945c is bytes4(keccak256(bytes("managerOf(address,uint256)"))) ie: IManager interface
                        if (_check3){
                            return IManager(_owner).managerOf(address(this), _id); // chain managerOf(nftContract, id) call if smart contract supports this interface
                        } else {
                            return _owner;
                        }
                    } else {
                        return _owner;
                    }
                } catch {
                    return _owner;
                }
            } else {
                return _owner;
            }
        } catch {
            return _owner;
        }    
    }
}
```

**概要**
指定されたトークンの管理者のアドレスを取得する関数。
管理者のアドレスを特定するためのロジックを提供します。

**詳細**
- デフォルトでは、トークンの所有者がそのトークンの管理者となります。

**引数**
- `_id`
    - トークンのID。

**戻り値**
- トークンの管理者のアドレス。

---

### changeDebtToken

```javascript
function changeDebtToken(uint256 _id, address _new) public onlyAdmin tokenExists(_id) {
    require(EIP6065MutableMetadata[_id].debt_amt == 0, "DEBT_AMT_NOT_ZERO");
    EIP6065MutableMetadata[_id].debt_token = _new;
    emit DebtTokenChanged(_id, _new);
}
```

**概要**
トークンの債務トークンを変更する関数。
トークンの債務トークンを新しいアドレスに変更できます。

**詳細**
- `_id`パラメータに指定されたトークンのIDに関連付けられた債務トークンを新しいアドレス`_new`に変更します。
- 既存の債務またはクレジットがトークンに存在しないことを確認します。

**引数**
- `_id`
    - トークンのID。
- `_new`
    - 新しい債務トークンのアドレス。

---

### balanceChange

```javascript
function balanceChange(uint256 _id, int256 _amt) public onlyAdmin tokenExists(_id) {
    require(_amt != 0, "NO_AMT");
    EIP6065Mutable memory _data = EIP6065MutableMetadata[_id];
    int256 _oldAmt = _data.debt_amt;
    _data.debt_amt += _amt;

    if (_amt > 0){ // debt added to token
        if (_oldAmt < 0){
            if (_data.debt_amt < 0){
                IERC20(_data.debt_token).transfer(ADMINISTRATOR, uint256(_amt)); // return entire _amt as it's all prior credit 
            }
            else {
                IERC20(_data.debt_token).transfer(ADMINISTRATOR, uint256(-1 * _oldAmt)); // return _oldAmt, as this credit has been zeroed, and debt added
            }
        }
    }
    else { // (_amt < 0) ie: credit added to token
        if (_data.debt_amt < 0){
            if (_oldAmt < 0){
                IERC20(_data.debt_token).transferFrom(ADMINISTRATOR, address(this), uint256(-1 * _amt)); // admin owes entire amt as credit
            }
            else {
                IERC20(_data.debt_token).transferFrom(ADMINISTRATOR, address(this), uint256(-1 * _data.debt_amt));
            }
        }
    }

    EIP6065MutableMetadata[_id].debt_amt = _data.debt_amt;
    emit DebtBalanceChanged(_id, _amt, _data.debt_amt);
}
```

**概要**
トークンの債務またはクレジットを変更する関数。
指定されたトークンの債務またはクレジットを変更できます。

**詳細**
- `_id`パラメータに指定されたトークンのIDに関連付けられた債務またはクレジットを変更します。
- `_amt`パラメータに指定された金額をトークンの債務またはクレジットに加算または減算します。
- トークンに債務が追加された場合、以前のクレジットを償還し、債務を追加するためのトランザクションを実行します。
    - 債務が追加されない場合、クレジットのトランザクションを実行します。
- トークンにクレジットが追加された場合、以前の債務を償還し、クレジットを追加するためのトランザクションを実行します。
    - クレジットが追加されない場合、債務のトランザクションを実行します。

**引数**
- `_id`
    - トークンのID。
- `_amt`
    - 変更する債務またはクレジットの金額。
    - 正の値は債務の追加、負の値はクレジットの追加を示します。

---

### payDebt

```javascript
function payDebt(uint256 _id, uint256 _amt) public tokenExists(_id) {
    EIP6065Mutable memory _data = EIP6065MutableMetadata[_id];
    require(_data.debt_amt > 0, "NO_DEBT");
    require(!_data.foreclosed, "FORECLOSED");
    if (_amt > uint256(_data.debt_amt)) _amt = uint256(_data.debt_amt);

    IERC20(_data.debt_token).transferFrom(msg.sender, ADMINISTRATOR, _amt);

    _data.debt_amt -= int256(_amt);

    EIP6065MutableMetadata[_id].debt_amt = _data.debt_amt;
    emit DebtPaid(_id, _amt, _data.debt_amt);
}
```

**概要**
トークンの債務を支払う関数。
指定されたトークンの債務を支払うことができます。

**詳細**
- `_id`パラメータに指定されたトークンのIDに関連付けられた債務を支払います。
- `_amt`パラメータに指定された金額を使用して、債務の一部またはすべてを支払います。
- トークンの債務が支払えない場合、支払える金額までの債務を支払います。

**引数**
- `_id`
    - トークンのID。
- `_amt`
    - 支払う債務の金額。

---

### claimCredit

```javascript
function claimCredit(uint256 _id) public {
    require(msg.sender == ownerOf(_id), "NOT_OWNER");

    EIP6065Mutable memory _data = EIP6065MutableMetadata[_id];
    require(_data.debt_amt < 0, "NO_CREDIT");

    EIP6065MutableMetadata[_id].debt_amt = 0;
    uint256 _transferAmt = uint256(-1 * _data.debt_amt);
    IERC20(_data.debt_token).transfer(msg.sender, _transferAmt);
    emit CreditClaimed(_id, _transferAmt);
}
```

**概要**
トークンのクレジットを請求する関数。

**詳細**
- この関数を呼び出すユーザーは、トークンの所有者である必要があります。
- トークンのクレジットが存在する場合、クレジットを請求し、トークンのクレジットをゼロに設定します。

**引数**
- `_id`
    - トークンのID。

---

### foreclose

```javascript
function foreclose(uint256 _id) public onlyAdmin tokenExists(_id) {
    require(!EIP6065MutableMetadata[_id].foreclosed, "FORECLOSED");
    EIP6065MutableMetadata[_id].foreclosed = true;
    emit Foreclosed(_id);
}
```

**概要**
トークンの強制売却を実行する関数。

**詳細**
- `_id`パラメータに指定されたトークンがすでに強制売却されていないことを確認します。
- トークンを強制売却済みに設定し、`Foreclosed`イベントを発行します。

**引数**
- `_id`
    - 強制売却するトークンのID。

---

### mint

```javascript
function mint(address _to, EIP6065Immutable calldata _immutableData, EIP6065Mutable calldata _mutableData) public onlyAdmin {
    uint256 _counter = COUNTER;
    _mint(_to, _counter);

    EIP6065ImmutableMetadata[_counter] = _immutableData;
    EIP6065MutableMetadata[_counter] = _mutableData;

    // not reasonable to overflow
    unchecked {
        COUNTER = _counter + 1;
    }
}
```

**概要**
新しいトークンを生成する関数。
新しいトークンを作成し、トークンのメタデータを設定できます。

**詳細**
- `_to`パラメータに指定されたアドレスに新しいトークンを発行します。
- `_immutableData`パラメータにはトークンの不変のメタデータが含まれます。
- `_mutableData`パラメータにはトークンの変更可能なメタデータが含まれます。
- トークンIDはカウンター`COUNTER`の値を使用して生成され、一意の値が割り当てられます。

**引数**
- `_to`
    - 新しいトークンを受け取るアドレス。
- `_immutableData`
    - トークンの不変のメタデータ。
- `_mutableData`
    - トークンの変更可能なメタデータ。

---

### burn

```javascript
function burn(uint256 _id) public onlyAdmin {
    require(ownerOf(_id) == ADMINISTRATOR, "ADMIN_NOT_OWNER");

    // recommend to clear data and info about NFT here too
    delete EIP6065ImmutableMetadata[_id];
    delete EIP6065MutableMetadata[_id];

    _burn(_id);
}
```

**概要**
トークンを焼却する関数。
指定されたトークンを消去し、それに関連するメタデータを削除できます。

**詳細**
- `_id`パラメータに指定されたトークンを所有するアドレスが`ADMINISTRATOR`であることを確認します。
- トークンに関連する不変のメタデータと変更可能なメタデータを削除し、トークンを焼却します。

**引数**
- `_id`
    - 焼却するトークンのID。

## 法的仕組みの導入

特定の企業がこのトークンの管理者として採用できる法的構造と実装について詳しく説明しています。
以下に示す構造は、2023年のアメリカでの不動産トークン化に関連し、その実施に特有のものです。

このセクションでは、具体的には2022年の規制環境におけるアメリカの不動産トークン化に使用できる法的標準の実装について詳細に説明しています。

![corporate-structure.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/570269/e6d1e153-fac2-c802-4a79-38fc378bbf08.png)

引用: https://eips.ethereum.org/EIPS/eip-6065

このトークンの法的構造は、以下の要点からなります。

1. 親会社と不動産管理者は、各々の不動産に対して独立した法的保護を提供するために、破産時に資産を切り離すための特別な形態である破産リモート型の**LLC**（有限責任会社）を保有しています。

2. 破産リモート型の**LLC**が、物理的な不動産の名義と権利書を保有し、不動産のNFT（トークン）および運用契約を発行する**DAO LLC**（分散型自治組織のLLC）の所有者かつマネージャーとなっています。

この法的構造により、以下の2つのポイントが実現されます。

1. 住宅所有者は、不動産の管理者が財政的な問題や破産に直面した場合でも保護されます。
管理者の破産や解散が発生した場合、NFTの所有者は**DAO LLC**の譲渡を受けたり、不動産の売却とその収益の分配を受ける権利があります。

2. 不動産権利の譲渡は、NFTの譲渡と同時に行われます。
NFTは、物理的な不動産の権利を請求し、権利書をNFTの所有者に譲渡する権利を表します。
これにより、不動産の権利はNFTの譲渡に伴ってデジタルで移転され、物件の法的所有者を更新するための繁雑な手続きが不要です。

**セキュリティノート**
プライベートキーがハッキングされた場合、会社は通常、ホームNFTを再発行できません。
そのため、ホームNFTの所有者は、彼ら自身が安全に保管できない場合に備えて、マルチシグウォレットや信託などのセキュリティオプションを検討することが重要です。
大規模なプロトコルのハッキングが発生した場合、会社は**Blocklist**機能を使用して資産を凍結し、元のホームNFTの所有者に再発行する可能性があります。
**Blocklist**機能の実装は、上記のリファレンス実装に将来的に追加される予定です。

# セキュリティ考慮事項

この基準を使用してNFTを統合するプロトコルに対するチェックと推奨事項は以下の通りです。
これらは、この基準を用いて資産を担保に貸し付けを行うアプリケーションに特に関連があります。

1. プロトコル統合者は、統合を希望する特定のNFTに対して、不動産の一意の識別子と運用契約のハッシュが不変であることを確認することが推奨されます。
これらの値が不変であることは、将来の譲渡者に対する合法性を保証するために重要です。
つまり、NFTに関連する不動産の特徴や契約が変更されないようにする必要があります。

2. プロトコル統合者は、このトークンの価値を正確に反映するために、`debtOf`（債務情報を提供する）の値を確認することが推奨されます。
これにより、NFTが現在の債務状態を適切に表すことができます。

3. プロトコル統合者は、トークンが依然として元の資産によってサポートされていることを確認するために、差し押さえ状態を確認することが推奨されます。
これにより、NFTが元の資産とのリンクを維持していることが確認されます。

4. リスクを軽減するために、プロトコル統合者は不可逆的なアクションを実行する前に時間の遅延を導入することを検討できます。
これは、ハッキングされたNFTがプロトコルにデポジットされた場合、資産が凍結される可能性に備えるためのものです。
資産の凍結は必須ではなく、資産管理者の実装に依存します。

# 引用

Alex (@Alex-Klasma), Ben Fusek (@bfusek), Daniel Fallon-Cyr (@dfalloncyr), "ERC-6065: Real Estate Token [DRAFT]," Ethereum Improvement Proposals, no. 6065, November 2022. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-6065.


# 最後に

今回は「**ERC721**規格を拡張して不動産NFTを管理するインターフェースを提案している**ERC6065**」についてまとめてきました！
いかがだったでしょうか？

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

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

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

https://chaldene.net/

https://zenn.dev/heku

https://mirror.xyz/0xcE77b9fCd390847627c84359fC1Bc02fC78f0e58