//tips
福岡市・北九州市 国家戦略特別区域 区域計画、是非成立させてほしい。素晴らしすぎる。頑張れ福岡。いい流れ。
https://www.chisou.go.jp/tiiki/kokusentoc/220531goudoukuikikaigi/shiryou3.pdf
https://www.chisou.go.jp/tiiki/kokusentoc/220531goudoukuikikaigi/shiryou2.pdf
ちなみに日本で暗号通貨ベンチャーができない一番要因はガバナンストークンへの課税。
日本では、法人が期末まで仮想通貨を保有していた場合、期末時(事業年度終了時)の時価が取得時の価格より高い場合、評価益が計上され所得に加えられ、利益が実現してないにもかかわらず税金の支払いが確定する。しかもガバナンストークンでの支払いが認められないので、市場にてトークンを換金する必要があり、トークン価格の崩壊などの負の連鎖がスタートダッシュの時点で生じることになる。
ちなみにシンガポールなどでは、長期保有資産扱いで税の支払いはゼロ。だから実質世界と戦うレベルのプロジェクトは海外で立ち上げざるを得ない。
下記参考。
https://coinpost.jp/?p=321032
https://coinpost.jp/?p=317371
https://www.digital.go.jp/assets/contents/node/basic_page/field_ref_resources/698fe447-2ae4-47f1-a948-896ffac46400/7496b1dc/20220414_meeting_conception_outline_03.pdf
//smart contract
構造理解継続。
balanceOf(address owner) は他のコインのように残高ではなく、ownerの保有するNFTの数を返すことになる。ownerが創造元となる0x0000…でなかったら実行するという制約をrequire(owner != address(0), "ERC721: address zero is not a valid owner”);でつけている。
function balanceOf(address owner) public view virtual override returns (uint256) {
require(owner != address(0), "ERC721: address zero is not a valid owner");
return _balances[owner];
}
balanceOfはview関数となり、データの読み取り専用なので、関数呼び出しにgas代は必要ない。
virtualはオーバーライド可能、 overrideは定義関数をオーバライドしてることを示す。セットで使われることになる。
少し思い出してきた。
virtualがついていることで、その関数は自由にカスタマイズしても良いということを、OpenZeppelinが示している。
ownerOf(uint256 tokenId)はtokenidから所持者のアドレスを取得する関数。
function ownerOf(uint256 tokenId) public view virtual override returns (address) {
address owner = _owners[tokenId];
require(owner != address(0), "ERC721: owner query for nonexistent token");
return owner;
}
対象のtokenIDが他の人にmintされていないかをEtherscanで確認できる。nameとsymbolはシンプルにNFTのシリーズ名とNFTをtoken名称にした場合のシンボルが表示される。
function name() public view virtual override returns (string memory) {
return _name;
}
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
https://etherscan.io/address/0xbd3531da5cf5857e7cfaa92426877b022e612cf8#readContract
ExistはtokenID=tokenIdを保有するaddressが、創造主でないかどうかの真偽値を返す。internalは定義したコントラクト、または、そのコントラクトを継承したコントラクトからの呼び出しを可能にする。
function _exists(uint256 tokenId) internal view virtual returns (bool) {
return _owners[tokenId] != address(0);
}
Internalを見たのでexternalも確認しておく、簡単にうと外部からの呼び出しとなるが、thisを使うと内部から外部的に関数を呼び出すことができ、その際のpublicとの違いとしては、
引数で渡された値をメモリに保存するかしないかの違いがあり、publicな関数を外部から呼ぶと引数の値を一度メモリに保存するが、externalな関数の場合は引数で渡された値はメモリに保存されることなく関数が処理されるのでgasがpublicよりも低くすむためpublicより使われるとのこと。
tokenURIは保持者が創造主アドレスではないことを確認した後に、baseURIを受け取ってbyte型にし、baseURIに中身があれば、abi.encodePackedで文字列を連結(baseURI + tokenId)して生成する。
function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
string memory baseURI = _baseURI();
return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : "";
}
function _baseURI() internal view virtual returns (string memory) {
return "";
}
ArtCollectibleではこの部分を加工してipfsにアップされているmetadataのurlをtokenURIとしている。
baseURIの部分にどのようにipfsの情報を反映させているのか気になったのでコントラクトを見返すもわからず探す。
メソッドで引数でipfsのハッシュを入力した部分があったかと思うのでそちらを確認。.claimItemメソッドを使用したかと思うので、見返すとtokenURIを引数に取っていることがわかる。つまり、ipfsのハッシュをtokenURI情報としてそのまま入れた形で対応していた。
function claimItem(string memory tokenURI) public returns (uint256) {
_tokenIds.increment();
uint256 newItemId = _tokenIds.current();
_safeMint(msg.sender, newItemId);
_setTokenURI(newItemId, tokenURI);
return newItemId;
}
次にmintに関連し、先にtranser関連を確認する。 _beforeTokenTransferは、トークンを転送する前処理として用意されたもので、このトークンを販売する際にはNFT作成者に一部のethの送付を行うなどのルールを作成できる。逆にfunction _afterTokenTransfer(は転送後のロジックを記載できる。
function _beforeTokenTransfer(
address from,
address to,
uint256 tokenId
) internal virtual {
この部分にロジックを自由に追加可能
}
Mintの中身を見ていく。
以前触れたがかなりシンプル。送付先が創造主でないことを確認し、tokenidを創造主以外が保有していないかを確認、これはexistでaddressが創造主のアドレスではないかどうかの真偽値で判別でき、existがfalseの場合は創造主、trueの場合は誰かが保有していることになる。
ここでは誰かが持っているものはmint済みと言うことなので、保有主が創造主アドレスであることが確認できる場合にmintさせている。
_beforeTokenTransfer(で転送代金などを設定できる。
その後は単純にNFTの保有数_balances[to] += 1と保有者の設定_owners[tokenId] = toをおこなっている。
eventである Transfer(address(0), to, tokenId)を実行するためにemitを先頭につける。
Solidity の event はバックエンドであるスマートコントラクトから、フロントエンドであるクライアントへ向けたメッセージの役割を担う。ここらへんも忘れてる感じ。イベント設定はフロントエンドへの伝達という点で特別なことはわかった。
event Transfer(address indexed _from, address indexed _to, uint _value);などを呼び出すとこのイベント情報を表示できる仕組みがあるはず。
それがprovider.once(次のblockからの通知を受け取るようにイベントをサブスクライブする)やcontract.on(先ほど定義したemit Tweetで発行されたイベントを受け取る)だとわかった。
確かにメタマスクへの通知は欲しいもんな。わかりみ。
function _mint(address to, uint256 tokenId) internal virtual {
require(to != address(0), "ERC721: mint to the zero address");
require(!_exists(tokenId), "ERC721: token already minted");
_beforeTokenTransfer(address(0), to, tokenId);
_balances[to] += 1;
_owners[tokenId] = to;
emit Transfer(address(0), to, tokenId);
_afterTokenTransfer(address(0), to, tokenId);
}