(この記事は2024年2月9日の情報をもとに記述されています)
最初に
今一部で話題のERC404のソースコードが存在したので、読んでみた。
このERC404は非公式の規格であり、コミュニティによってまとめられたものではないので、その点ご注意ください。
そもそもERC404ってなに?
このポストによると、
- ERC404は新しいトークン標準であり、NFTとERC20の特性を組み合わせたものである。
- ERC20は代替可能トークンであり、供給が豊富で一意性がない。
- ERC721は代替不可能トークン(NFT)であり、一意のIDを持つ低供給のトークンである。
- ERC404はERC721とERC20のハイブリッドであり、NFTトークン内に分割可能性を持たせている。
- ERC404トークンは分数の取引が可能であり、所有権には最低限の基本単位が必要。
- ERC404の取引は通常のNFT取引よりもガスコストが高い。
- ERC404トークンはERC20またはERC721の標準に厳密に準拠しておらず、独自の仕様を持っている。
- ERC404のコードには、所有者の追跡やトークンの移動を行うための多くのマッピングが含まれる。
- ERC404の社会的な合意が重要であり、標準番号は任意のものである。
らしい。言っていることがわかるところとわからないことがあるが、取り急ぎ、コードみてキャッチアップしていこうと思う。
404という数字もわかった上でつけてるんやろうなぁ。
早速コードを読んでみる
ERC404のコードと思われるものを読んでみる
L2
soliditypragma solidity ^0.8.0;
「^0.8.0」の時点でセンスがない。
不安が過ぎる。
L4〜L39
solidityabstract contract Ownable { event OwnershipTransferred(address indexed user, address indexed newOwner); 〜〜〜〜〜省略〜〜〜〜〜〜 function revokeOwnership() public virtual onlyOwner { owner = address(0); emit OwnershipTransferred(msg.sender, address(0)); } }
いわゆるOwnable、おかしなところはない。
L52-L67
solidity/// @notice ERC404 /// A gas-efficient, mixed ERC20 / ERC721 implementation /// with native liquidity and fractionalization. /// /// This is an experimental standard designed to integrate /// with pre-existing ERC20 / ERC721 support as smoothly as /// possible. /// /// @dev In order to support full functionality of ERC20 and ERC721 /// supply assumptions are made that slightly constraint usage. /// Ensure decimals are sufficiently large (standard 18 recommended) /// as ids are effectively encoded in the lowest range of amounts. /// /// NFTs are spent on ERC20 functions in a FILO queue, this is by /// design. ///
開発者向けコメント。
ERC20とERC721の混合実装であること、実験的な企画であることが記述されている。
L68行目
solidityabstract contract ERC404 is Ownable {
抽象コントラクト、このコントラクトを継承して、ERC404の機能を利用する仕組み。
L70-L94
各種イベント宣言。
L97-L101
各種エラー宣言。
L103からL114
solidity// Metadata /// @dev Token name string public name; /// @dev Token symbol string public symbol; /// @dev Decimals for fractional representation uint8 public immutable decimals; /// @dev Total supply in fractionalized representation uint256 public immutable totalSupply;
name、symbol、decimals、totalSupplyといった、ERC20のいつもの変数が宣言されている。totalSupplyがimmutableになっているのは、一時的なものなのだろうか。
このコントラクト内ではtotalSupplyの参照を一切していない。
仮にmintを永遠に続けてtotalSupplyの値を超えても問題ない作り。
なんだこれはバグなのか?それともわざとか?
L117
solidityuint256 public minted;
mintedと言う変数がある。ERC721で言うところのidカウンター。
L120-L139
solidity/// @dev Balance of user in fractional representation mapping(address => uint256) public balanceOf; /// @dev Allowance of user in fractional representation mapping(address => mapping(address => uint256)) public allowance; /// @dev Approval in native representaion mapping(uint256 => address) public getApproved; /// @dev Approval for all in native representation mapping(address => mapping(address => bool)) public isApprovedForAll; /// @dev Owner of id in native representation mapping(uint256 => address) internal _ownerOf; /// @dev Array of owned ids in native representation mapping(address => uint256[]) internal _owned; /// @dev Tracks indices for the _owned mapping mapping(uint256 => uint256) internal _ownedIndex;
ERC721で言うところの、所有権情報や残高を管理する変数群。
L142
soliditymapping(address => bool) public whitelist;
whitelistを管理する変数が存在する。
whitelistで何をするかは後述。
L145-156
solidityconstructor( string memory _name, string memory _symbol, uint8 _decimals, uint256 _totalNativeSupply, address _owner ) Ownable(_owner) { name = _name; symbol = _symbol; decimals = _decimals; totalSupply = _totalNativeSupply * (10 ** decimals); }
コンストラクタ、nameや小数点以下桁数などのメタデータをセット。
L160-162
solidityfunction setWhitelist(address target, bool state) public onlyOwner { whitelist[target] = state; }
whitelistのセッター。
マークルルートを登録しないあたり、ホワイトリストはごく少数と言う運用なのだろうか。
ちなみにwhitelistに追加されていないアドレスがトークンをtrasnferすると、所有残高の削減とともに、idに対する所有権のdeleteが行われる。
whitelistに追加されているアドレスがトークンをtrasnferすると、もちろん所有残高の削減はするのだが、idに対する所有権は残ったままである。
L165-171
ownerOf関数、特に違和感はない。
L174
tokenURI関数、継承先でちゃんと実装しろよとのこと。
L178-199
solidityfunction approve( address spender, uint256 amountOrId ) public virtual returns (bool) { if (amountOrId <= minted && amountOrId > 0) { address owner = _ownerOf[amountOrId]; if (msg.sender != owner && !isApprovedForAll[owner][msg.sender]) { revert Unauthorized(); } getApproved[amountOrId] = spender; emit Approval(owner, spender, amountOrId); } else { allowance[msg.sender][spender] = amountOrId; emit Approval(msg.sender, spender, amountOrId); } return true; }
approve関数
ERC721っぽく、指定したidをtransfer許可したり、指定した量をtransfer許可したりしてる
と言うか同じ引数をidだったり量だったり使い分けてるのがきもいし、その判断基準がmint数より大きいか否かって言うのも違和感がある。
L202-206
setApprovalForAll関数、まぁそんな感じのもの
L210-263
solidityfunction transferFrom( address from, address to, uint256 amountOrId ) public virtual { if (amountOrId <= minted) { if (from != _ownerOf[amountOrId]) { revert InvalidSender(); } if (to == address(0)) { revert InvalidRecipient(); } if ( msg.sender != from && !isApprovedForAll[from][msg.sender] && msg.sender != getApproved[amountOrId] ) { revert Unauthorized(); } balanceOf[from] -= _getUnit(); unchecked { balanceOf[to] += _getUnit(); } _ownerOf[amountOrId] = to; delete getApproved[amountOrId]; // update _owned for sender uint256 updatedId = _owned[from][_owned[from].length - 1]; _owned[from][_ownedIndex[amountOrId]] = updatedId; // pop _owned[from].pop(); // update index for the moved id _ownedIndex[updatedId] = _ownedIndex[amountOrId]; // push token to to owned _owned[to].push(amountOrId); // update index for to owned _ownedIndex[amountOrId] = _owned[to].length - 1; emit Transfer(from, to, amountOrId); emit ERC20Transfer(from, to, _getUnit()); } else { uint256 allowed = allowance[from][msg.sender]; if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amountOrId; _transfer(from, to, amountOrId); } } /// @notice Function for fractional transfers function transfer( address to, uint256 amount ) public virtual returns (bool) { return _transfer(msg.sender, to, amount); } /// @notice Function for native transfers with contract support function safeTransferFrom( address from, address to, uint256 id
transferFrom関数。approveと同じく引数をidだったり量だったり状況によって使い分けてて、
すごく違和感がある。
L266-L271
transfer関数。中身はERC20っぽいが、送信元や送信先がwhitelistに追加されているか否かで
所有権を削除するかどうかの挙動が変わる。
L274-L306
safeTransferFrom関数
振り返り
コードを見た上で当初の思想をもう一度確認してみた。
- ERC404は新しいトークン標準であり、NFTとERC20の特性を組み合わせたものである。
→そうですね、混ざってますね
- ERC20は代替可能トークンであり、供給が豊富で一意性がない。
→ERC20はそうですね
- ERC721は代替不可能トークン(NFT)であり、一意のIDを持つ低供給のトークンである。
→ERC721はそうですね
- ERC404はERC721とERC20のハイブリッドであり、NFTトークン内に分割可能性を持たせている。
→はい、至る所にきもい分岐がありました
- ERC404トークンは分数の取引が可能であり、所有権には最低限の基本単位が必要。
→そうですね、decimalsが設定されていましたので、ERC721の要素を保持する割には、分数というか、小数の取引が可能ですね。そしてwhitelistに加えられてない場合、transferすると所有者権限も消えますね。
- ERC404の取引は通常のNFT取引よりもガスコストが高い。
→はい、ERC20の処理とERC721の処理があるので、ガスがその分増えますね。
- ERC404トークンはERC20またはERC721の標準に厳密に準拠しておらず、独自の仕様を持っている。
→そうですね、allowanceのインターフェースが違いますね。でもOpenSeaでは扱えそうな顔をしています
- ERC404のコードには、所有者の追跡やトークンの移動を行うための多くのマッピングが含まれる。
→そうですね、先ほどのガスが高い理由になりますね。
- ERC404の社会的な合意が重要であり、標準番号は任意のものである。
→そうですねとしか言いようないですね。
所感
まだまだ発展途上なので、手を出す場合はなくなっていいお金の分だけどうぞ。