logo

ERC404コードリーディング

〜そういえば昔503エラーっていうトークンがあったな〜
profile photo
TaniguchiAkira
(この記事は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という数字もわかった上でつけてるんやろうなぁ。
Image without caption

早速コードを読んでみる

L2
solidity
pragma solidity ^0.8.0;
「^0.8.0」の時点でセンスがない。
不安が過ぎる。
L4〜L39
solidity
abstract 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行目
solidity
abstract 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
solidity
uint256 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
solidity
mapping(address => bool) public whitelist;
whitelistを管理する変数が存在する。
whitelistで何をするかは後述。
L145-156
solidity
constructor( 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
solidity
function 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
solidity
function 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
solidity
function 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の社会的な合意が重要であり、標準番号は任意のものである。
  →そうですねとしか言いようないですね。

所感

まだまだ発展途上なので、手を出す場合はなくなっていいお金の分だけどうぞ。

参考リンク

Related posts
post image
Solidity
OpenZeppelin
AA
OpenZeppelin 5.2.0が出るよ
post image
Solidity
Optimism
Arbitrum
L2
Wormhole
Layer0
オレオレインターフェースからERC7802へ
ユニバーサル規格は大事
post image
Solidityも早く1.0.0出て欲しい
Powered by Notaku