はじめに
『DApps開発入門』という本や色々記事を書いているかるでねです。
今回は、NFTの派生作品に関するライセンス契約を管理する仕組みを提案しているERC5635についてまとめていきます!
以下にまとめられているものを翻訳・要約・補足しながらまとめていきます。
他にも様々なEIPについてまとめています。
概要
ERC5635は、NFTの派生作品に関するライセンス契約を登録・参照できる「ライセンス・オラクル(NFT Licensing Oracle)」を標準化する提案です。
派生作品としてのNFT(dNFT)と元のNFT(oNFT)
ERC5635では、「派生作品としてのNFT」を dNFT(derivative NFT)と呼び、元になった「オリジナルのNFT」を oNFT(original NFT)と呼んでいます。
例えば、有名なNFTアート作品(oNFT)をもとにして、新しい作品(dNFT)を別のクリエイターが制作する場合が該当します。
ライセンス契約とは
oNFTの保有者、つまりオリジナル作品の権利を持っている人(licensor)は、別のクリエイター(licensee)に対して、dNFTを制作する権利を与えることができます。
このとき、licenseeは作品を作る代わりに、報酬(ロイヤリティ)を支払うのが一般的です。
ERC5635では、このような取引の条件や合意内容(ライセンス契約)を登録・管理し、他の人もその情報を参照できるようにする仕組みを定義します。
今までは、NFTの派生作品が誰からの許可を得て作られたのかが不透明で、著作権的なトラブルの原因にもなっていました。
ERC5635を使うと、「このdNFTは誰のoNFTをもとに、誰の許可を得て作られたのか」が簡単に確認できるようになります。
つまり、NFTの世界でも「ライセンス契約の見える化」が実現できるようになります。
仕様
NFTライセンス標準における3つの役割
この規格には3つの主要な役割があります。
-
oNFT
- 元になるNFT。
- これを持っている人がlicensor(権利を与える側)になります。
- どんなNFTでもoNFTになれます。
-
dNFT
- oNFTをもとに作られた派生作品のNFT。
- これを持っている人がlicensee(権利を受ける側)です。
-
Registry
- oNFTの持ち主が発行したことを確認できるコントラクト。
- これが「このライセンス情報は正しい」と証明する役割を果たします。
これら3つがうまく連携することで、NFTのライセンス管理がスムーズになります。
IERC5635DNFT
dNFTコントラクトは、以下の2つのインターフェースを実装する必要があります。
-
IERC5635DNFT
- この規格で定められたdNFT用インターフェース。
-
IERC165
- あるコントラクトが特定のインターフェースを実装しているかどうかを調べられる仕組み。
pragma solidity ^0.6.0;
import "./IERC165.sol";
///
/// @notice Interface of NFT derivatives (dNFT) for the NFT Licensing Standard
/// @dev The ERC-165 identifier for this interface is 0xd584841c.
interface IERC5635DNFT is IERC165 {
/// ERC165 bytes to add to interface array - set in parent contract
/// implementing this standard
///
/// bytes4(keccak256("IERC5635DNFT{}")) == 0xd584841c
/// bytes4 private constant _INTERFACE_ID_IERC5635DNFT = 0xd584841c;
/// _registerInterface(_INTERFACE_ID_IERC5635XDNFT);
/// @notice Get the number of credentials.
/// @param _tokenId - ID of the dNFT asset queried
/// @return _number - the number of credentials
function numberOfCredentials(
uint256 _tokenId
) external view returns (
uint256 _number
);
/// @notice Called with the sale price to determine how much royalty is owed and to whom.
/// @param _tokenId - ID of the dNFT asset queried
/// @param _credentialId - ID of the licensing agreement credential, the max id is numberOfCredentials(_tokenId)-1
/// @return _oNFT - the oNFT address where the licensing from
/// @return _tokenID - the oNFT ID where the licensing from
/// @return _registry - the address of registry which can verify this credential
function authorizedBy(
uint256 _tokenId,
uint256 _credentialId
) external view returns (
address _oNFT,
uint256 _tokenId,
address _registry
);
}
interface IERC165 {
/// @notice Query if a contract implements an interface
/// @param interfaceID The interface identifier, as specified in ERC-165
/// @dev Interface identification is specified in ERC-165. This function
/// uses less than 30,000 gas.
/// @return `true` if the contract implements `interfaceID` and
/// `interfaceID` is not 0xffffffff, `false` otherwise
function supportsInterface(bytes4 interfaceID) external view returns (bool);
}
このインターフェースには、以下の関数が定義されています。
numberOfCredentials
function numberOfCredentials(
uint256 _tokenId
) external view returns (
uint256 _number
);
指定したdNFTがいくつライセンス(証明)を持っているかを返す関数。
例えば、「このdNFTは3つのoNFTから作られている」なら、3
が返ってきます。
authorizedBy
function authorizedBy(
uint256 _tokenId,
uint256 _credentialId
) external view returns (
address _oNFT,
uint256 _tokenId,
address _registry
);
特定のdNFTとその中のライセンス証明(credential
)について問い合わせて以下の情報を取得できる関数。
- どのoNFTからライセンスを受けたか(oNFTのアドレス)。
- oNFTのトークンID。
- それを証明できるレジストリのアドレス。
この関数を使えば、「このdNFTはどの作品を使って作られていてその使用許可は本物なのか?」というのがすぐにわかるようになります。
IERC165とは?
IERC165
はコントラクトがあるインターフェースを実装しているかどうかを確認できる仕組みです。
この規格を使うことで、例えば「このdNFTコントラクトはIERC5635DNFT
を実装しているか?」というチェックが可能になります。
ERC165については以下の記事を参考にしてください。
IERC5635Registry
IERC5635Registry
は、NFTのライセンス情報を管理・検証する役割を担います。
全てのレジストリコントラクトは、以下の2つのインターフェースを実装する必要があります。
-
IERC5635Registry
- ライセンス情報を照会・検証するためのコア機能を定義。
-
IERC165
- コントラクトがどのインターフェースに対応しているかを確認できる仕組み。
IERC5635Registry
インターフェースは、dNFTとoNFTの間に結ばれたライセンス契約の有無や詳細、ロイヤリティの情報などを取得するための関数を定義しています。
pragma solidity ^0.6.0;
import "./IERC165.sol";
///
/// @dev Interface of NFT derivatives (dNFT) for the NFT Licensing Standard
/// Note: the ERC-165 identifier for this interface is 0xb5065e9f
interface IERC5635Registry is IERC165 {
/// ERC165 bytes to add to interface array - set in parent contract
/// implementing this standard
///
/// bytes4(keccak256("IERC5635Registry{}")) == 0xb5065e9f
/// bytes4 private constant _INTERFACE_ID_IERC5635Registry = 0xb5065e9f;
/// _registerInterface(_INTERFACE_ID_IERC5635Registry);
// TODO: Is the syntax correct?
enum LicensingAgreementType {
NonExclusive,
Exclusive,
Sole
}
/// @notice
/// @param _dNFT -
/// @param _dNFT_Id -
/// @param _oNFT -
/// @param _oNFT_Id -
/// @return _licensed -
/// @return _tokenID - the oNFT ID where the licensing from
/// @return _registry - the address of registry which can verify this credential
function isLicensed(
address _dNFT,
uint256 _dNFT_Id,
address _oNFT,
uint256 _oNFT_Id
) external view returns (
bool _licensed
);
/// @return _licenseIdentifier - the identifier, e.g. `MIT` or `Apache`, similar to `SPDX-License-Identifier: MIT` in SPDX.
function licensingInfo(
address _dNFT,
uint256 _dNFT_Id,
address _oNFT,
uint256 _oNFT_Id
) external view returns (
bool _licensed,
address _licensor,
uint64 _timeOfSignature,
uint64 _expiryTime,
LicensingAgreementType _type,
string _licenseName,
string _licenseUri //
);
function royaltyRate(
address _dNFT,
uint256 _dNFT_Id,
address _oNFT,
uint256 _oNFT_Id
) external view returns (
address beneficiary,
uint256 rate // The decimals is 9, means to divide the rate by 1,000,000,000
);
}
LicensingAgreementType
enum LicensingAgreementType {
NonExclusive,
Exclusive,
Sole
}
ライセンスの種類を示す列挙型(enum
)。
-
NonExclusive
- 非独占ライセンス。
- 他の人にも同じライセンスを与えることができます。
-
Exclusive
- 独占ライセンス。
- 他の誰にもライセンスは与えません。
-
Sole
- 単独ライセンス。
- ライセンサー自身は使うが、他の人には与えません。
isLicensed
function isLicensed(
address _dNFT,
uint256 _dNFT_Id,
address _oNFT,
uint256 _oNFT_Id
) external view returns (bool _licensed);
指定したdNFTが、指定したoNFTに対して正式なライセンスを持っているかどうかを確認する関数。
返り値が true
なら、ライセンス契約が存在しています。
licensingInfo
function licensingInfo(
address _dNFT,
uint256 _dNFT_Id,
address _oNFT,
uint256 _oNFT_Id
) external view returns (
bool _licensed,
address _licensor,
uint64 _timeOfSignature,
uint64 _expiryTime,
LicensingAgreementType _type,
string _licenseName,
string _licenseUri
);
ライセンスの詳細情報を取得する関数。
-
licensed
- ライセンスが有効かどうか。
-
licensor
- ライセンスを与えたアドレス(oNFTの保有者)。
-
timeOfSignature
- 契約が締結された時間(UNIXタイム)。
-
expiryTime
- ライセンスの有効期限(UNIXタイム)。
-
type
- ライセンスの種類(非独占・独占・単独)。
-
licenseName
- ライセンス名(例:MIT、CC-BYなど)。
-
licenseUri
- ライセンス文書へのリンク。
これにより、ライセンスの正当性と条件を明確に把握できます。
royaltyRate
function royaltyRate(
address _dNFT,
uint256 _dNFT_Id,
address _oNFT,
uint256 _oNFT_Id
) external view returns (
address beneficiary,
uint256 rate
);
指定されたdNFTとoNFTの間で発生するロイヤリティ情報を取得する関数。
-
beneficiary
- ロイヤリティを受け取るアドレス。
-
rate
- ロイヤリティ率(小数点以下9桁として扱い、
1,000,000,000
で割る必要あり)。
- ロイヤリティ率(小数点以下9桁として扱い、
例えば、rate = 50000000
なら、5%
(0.05
)のロイヤリティという意味になります。
IERC5635Licensing
IERC5635Licensing
は、NFTのライセンス契約を実際に発行・管理するための追加インターフェースです。
このインターフェースの実装は任意です。
ただし、IERC5635Licensing
を実装すると、レジストリが単なる照会機能だけでなく、ライセンス契約を発行・承認・制御する機能も持つようになります。
このインターフェースは IERC165
と IERC5635Registry
を継承しています。
pragma solidity ^0.6.0;
import "./IERC165.sol";
///
///
interface IERC5635Licensing is IERC165, IERC5635Registry {
event Licence(address indexed _oNFT, uint256 indexed _oNFT_Id, address indexed _dNFT, uint256 indexed _dNFT_Id, uint64 _expiryTime, LicensingAgreementType _type, string _licenseName, string _licenseUri);
event Approval(address indexed _oNFT, address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
event ApprovalForAll(address indexed _oNFT, address indexed _owner, address indexed _operator, bool _approved);
function licence(address indexed _oNFT, uint256 indexed _oNFT_Id, address indexed _dNFT, uint256 indexed _dNFT_Id, uint64 _expiryTime, LicensingAgreementType _type, string _licenseName, string _licenseUri) external payable; //TODO: mortgages or not?
function approve(address indexed _oNFT, address _approved, uint256 _tokenId) external payable; //TODO: why payable?
function setApprovalForAll(address indexed _oNFT, address _operator, bool _approved) external;
function getApproved(address indexed _oNFT, uint256 _tokenId) external view returns (address);
function isApprovedForAll(address indexed _oNFT, address _owner, address _operator) external view returns (bool);
}
イベント
Licence
event Licence(
address indexed _oNFT,
uint256 indexed _oNFT_Id,
address indexed _dNFT,
uint256 indexed _dNFT_Id,
uint64 _expiryTime,
LicensingAgreementType _type,
string _licenseName,
string _licenseUri
);
新しいライセンスが発行されたときに発行されるイベント。
誰が誰にどんな条件でライセンスを出したのかを記録します。
Approval
event Approval(
address indexed _oNFT,
address indexed _owner,
address indexed _approved,
uint256 indexed _tokenId
);
特定のトークンに対して、ライセンスの発行を代理で行える権限(approve
)が与えられたときに発行されるイベント。
ApprovalForAll
event ApprovalForAll(
address indexed _oNFT,
address indexed _owner,
address indexed _operator,
bool _approved
);
オーナーがあるオペレーターに対して、「自分の全NFTについてライセンスの操作を任せる」を設定したときに発行されるイベント。
関数
licence
function licence(
address _oNFT,
uint256 _oNFT_Id,
address _dNFT,
uint256 _dNFT_Id,
uint64 _expiryTime,
LicensingAgreementType _type,
string _licenseName,
string _licenseUri
) external payable;
oNFT に基づいて dNFT にライセンスを発行する関数。
有効期限・ライセンスタイプ・名称・URI などを指定できます。
payable
になっているのは、将来的にライセンス発行に手数料やデポジットが必要になる可能性があるためです。
approve
function approve(address _oNFT, address _approved, uint256 _tokenId) external payable;
特定のNFT(oNFTの指定トークン)に関して、他者にライセンス発行の権限を委任する関数。
こちらも payable
になっているため、将来的に承認処理に対してコストがかかる設計が想定されている可能性があります。
setApprovalForAll
function setApprovalForAll(address _oNFT, address _operator, bool _approved) external;
複数のoNFTを一括で操作できるように特定のオペレーターに権限を与える関数。
getApproved
function getApproved(address _oNFT, uint256 _tokenId) external view returns (address);
特定のNFTに対して誰が代理権限を持っているかを確認できる関数。
isApprovedForAll
function isApprovedForAll(address _oNFT, address _owner, address _operator) external view returns (bool);
指定したオペレーターが、オーナーのすべてのoNFTに対して操作権限を持っているかを確認できる関数。
補足
authorizedBy
とレジストリの役割
dNFTコントラクトに実装される authorizedBy
関数を使うと、そのdNFTがどのoNFTに基づいてライセンスを受けているか、つまりどんな契約に基づいて作られたのかを調べることができます。
そして、そこで参照されたライセンス情報(credential
)は、レジストリコントラクトを使って検証可能です。
また、licensingRoyalty
関数を使えば、誰でもロイヤリティ情報を取得できます。
これにより、マーケットプレイスやユーザーは、「この作品の収益は誰に分配されるのか?」を確認できます。
強制力はないが準拠を推奨
ERC5635は、オンチェーンでライセンスを強制するものではありません。
これは ERC2981(NFTのロイヤリティ仕様)と同じ考え方で、技術的に強制はできないがNFTマーケットなどがERC5635に準拠することを推奨しています。
ライセンス発行フェーズと情報参照フェーズ
dNFTが発行(mint)されるタイミングで以下の2つのフェーズに分かれます。
- Licensing stage
dNFTが発行される前。IERC5635Licensing
インターフェースを使用してライセンスを発行する段階。
- Discovery stage
dNFTが発行された後。IERC5635DNFT
と IERC5635Registry
を使用して、ライセンスの情報を参照・確認する段階。
これにより、「いつ、どの段階で、どのインターフェースを使えばよいか」が明確になります。
設計判断:ライセンスの受益者(beneficiary)
NFTの所有者が変わった場合、ライセンスの権利も新しい所有者に自動的に移ります。
そのため、ライセンスの受益者は「現在のNFT所有者」となります。
このように設計することで、NFTの売買後もスムーズにライセンスの扱いが引き継がれるようになります。
CantBeEvilライセンスとの違い
よく使われている「CantBeEvilライセンス」は、NFTのクリエイターが所有者に対して与える権利を記述したものです。
一方で、ERC5635で扱っている「ライセンス契約」は、oNFTの保有者とdNFTを作る人の間で結ばれる契約です。
つまり、CantBeEvilは一方向的な「使用許可の宣言」にすぎず、法的なライセンス契約の代わりにはなりません。
設計判断:承認レベルの使い分け
oNFTの保有者は、自分に代わってライセンス契約を発行できるよう、他のアドレスに権限を与えることができます。
この承認レベルは2つあります。
-
approve
- 特定のトークンIDだけに対してライセンス発行を許可します。
-
setApprovalForAll
- 自分が持っているすべてのNFTに対して、指定した相手にまとめてライセンス発行権限を与えます。
このように権限の細かいコントロールができる設計になっています。
互換性
ERC5635は、ERC721、ERC1155、ERC2981と互換性があります。
ERC2981については以下の記事を参考にしてください。
引用
Timi (@0xTimi), 0xTriple7 (@ysqi), "ERC-5635: NFT Licensing Agreements [DRAFT]," Ethereum Improvement Proposals, no. 5635, August 2022. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-5635.
最後に
今回は「NFTの派生作品に関するライセンス契約を管理する仕組みを提案しているERC5635」についてまとめてきました!
いかがだったでしょうか?
質問などがある方は以下のTwitterのDMなどからお気軽に質問してください!
他の媒体でも情報発信しているのでぜひ他も見ていってください!