初心者文系主婦がブロックチェーンを学ぶブログ

IT革命のビッグウェーブに乗り遅れた主婦が、ブロックチェーン革命の波にのるべく、ブロックチェーン技術を学ぶブログです。

【CryptZombies】レッスン5チャプター8:ERC721 takeOwnership

takeOwnership関数は、msg.senderがそのトークン/ゾンビを受け取ることを承認されているか確認し、承認されていれば_transfer関数を呼び出すだけでOKです。

テストの実行

①初めに、requireステートメントを使って、zombieApprovalsでキーが_tokenIdの場合にmsg.senderと同等となるようにせよ。こうしてもしmsg.senderがトークンを受け取ることが承認されていなかった場合、エラーが投げられる。

function takeOwnership(uint256 _tokenId) public {
    require(zombieApprovals[_tokenId] == msg.sender);
  }

②_transfer関数を呼び出すには、トークン所有者のアドレスを知る必要がある。(_fromの引数として必要だ)。幸い、このアドレスはownerOf関数で参照可能だ。ではownerというaddress変数を宣言し、それがownerOf(_tokenId)と同等となるようにせよ。

ownerOf関数はerc721.solに格納されているので、そちらを参照します。

function takeOwnership(uint256 _tokenId) public {
    require(zombieApprovals[_tokenId] == msg.sender);
    address owner = ownerOf(_tokenId);
  }

③最後に、_transfer関数を呼び出して、必要な情報全てを渡せ。(ここでは、_toの引数にmsg.senderを使う。なぜなら関数を呼び出しているのは、トークンが送られるべき者だからだ)。

function takeOwnership(uint256 _tokenId) public {
    require(zombieApprovals[_tokenId] == msg.sender);
    address owner = ownerOf(_tokenId);
    _transfer(owner, msg.sender, _tokenId);
  }

注: 上の2と3のステップは1行のコードにて行うことができるが、少しばかり読みやすくするために分割している。


お疲れさまでした!


<参考>
CryptoZombies - イーサリアム上でゲームを開発する方法を学習。Powered by Loom Network

【CryptZombies】レッスン5チャプター7:ERC721 Approve

approve / takeOwnershipの2つの関数で、トランスファーは2段階で発生します。(チャプター5参照)
トークン所有者がapprove関数を呼び出し、それに新たな所有者のaddressと彼に送る_tokenIdの情報を与える。
②新たな所有者がtakeOwnership関数を_tokenIdで呼び出すと、コントラクトは彼が承認済みの新たな所有者であるかを確認してから、彼にトークンを移転する。
これは2度の関数呼び出しで発生するので、その2度の間で誰がどのトークンについて受け取りを承認されているか記録するデータ構成が必要です。

テストの実行

①まず初めに、zombieApprovalsというマッピングを定義せよ。uintからaddressを指し示すマップにすること。こうすることで、誰かが_tokenIdでtakeOwnership関数を呼び出すときに、マッピングで誰がトークン受け取りを承認されているかをさっとチェック可能だ。

mapping (uint => address) zombieApprovals;

②approve関数では、トークン所有者のみが、受け手にトークンを受け取る許可を与えられるようにしておきたい。 なので、approve関数にonlyOwnerOf修飾詞を加えよう。

function approve(address _to, uint256 _tokenId) public onlyOwnerOf(_tokenId) {
    // 3. ここに関数を定義すること
  }

③関数の中身に、_tokenIdがキーのzombieApprovalsが_toアドレスと同等となるよう設定せよ。

function approve(address _to, uint256 _tokenId) public onlyOwnerOf(_tokenId) {
    zombieApprovals[_tokenId] = _to;
  }

④最後に、ERC721規格にはApprovalイベントがあるから、関数の最後でこれを発生させ流ようにせよ。erc721.solをチェックして_ownerの引数にmsg.senderを使うよう確かめること。

 function approve(address _to, uint256 _tokenId) public onlyOwnerOf(_tokenId) {
    zombieApprovals[_tokenId] = _to;
    Approval(msg.sender, _to, _tokenId);
  }

お疲れさまでした!


<参考>
CryptoZombies - イーサリアム上でゲームを開発する方法を学習。Powered by Loom Network

【CryptZombies】レッスン5チャプター5&6:ERC721トランスファーのロジック

チャプター5:ERC721規格の所有権の移転

ERC721規格は、2つの異なるトークン移転法を持ちます。

function transfer(address _to, uint256 _tokenId) public;
function approve(address _to, uint256 _tokenId) public;
function takeOwnership(uint256 _tokenId) public;

①一番目の方法は、トークン所有者が送り先のaddress、そして送りたいトークンの_tokenId(ゾンビID)を送ってtransfer関数を呼び出すものです。
②二番目の方法は、トークン所有者がまずapprove関数を呼び出し、一番目と同じ情報を関数に送ります。
すると、コントラクトが誰がトークン受け取りを許可されたのかを、通常はmapping (uint256 => address)にて記録します。
さらに誰かがtakeOwnershipを呼び出すと、コントラクトはそのmsg.senderがトークンを受け取ることを所有者から承認されているかをチェックし、承認済みの場合は彼にトークンを移転します。

transferとtakeOwnershipのどちらも同じ転送ロジックを含みますが、順序が逆になります。(ひとつはトークンの送り手が関数を呼び出すケース、もうひとつはトークンの受け手が関数を呼び出すケース)。

したがって、このロジックを独自のプライベート関数 _transferに抽象化し、これを両方の関数で呼び出すようにします。そうすれば、同じコードを2回繰り返さなくて済むので。

テストの実行

_transferのロジックを定義しよう。

①_transferという名前の関数を定義せよ。これはaddress _from、address _toそしてuint256 _tokenIdの3つの引数を受け取り、private関数とすること。

function _transfer(address _from, address _to, uint256 _tokenId) private {
    
  }

②オーナーシップが変わるとき、2つのマッピングが更新される: ownerZombieCount (所有者のゾンビ保有数を記録する)と、zombieToOwner (誰がどのゾンビを所有しているか記録する)。

まずこの関数で行うべきは、ゾンビを 受け取る 者(address _to)のownerZombieCountを加算することだ。加算には++を使うように。

function _transfer(address _from, address _to, uint256 _tokenId) private {
    ownerZombieCount[_to]++;
  }

③次にゾンビを 送る 者(address _from)のownerZombieCountを 減らす ことが必要だ。--を使うこと。

function _transfer(address _from, address _to, uint256 _tokenId) private {
    ownerZombieCount[_to]++;
    ownerZombieCount[_from]--;
  }

④最後にzombieToOwnerマッピングを変更したい。この_tokenIdが今度は_toを指し示すようにせよ。

function _transfer(address _from, address _to, uint256 _tokenId) private {
    ownerZombieCount[_to]++;
    ownerZombieCount[_from]--;
    zombieToOwner[_tokenId] = _to;
  }

⑤おっと、まだ最後ではなかった!もう一つやらねばならぬことがある。

ERC721規格は、Transferイベントを含む。この関数の最終行で、適切な情報を渡してTransferイベントを起こさなくてはならない。erc721.solを見て、呼び出しに際してどんな引数を求められているのかをチェックして実装せよ。

erc721.solのTransferイベントは次の通りです。

event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);

したがって、上記に該当する引数を渡します。

function _transfer(address _from, address _to, uint256 _tokenId) private {
    ownerZombieCount[_to]++;
    ownerZombieCount[_from]--;
    zombieToOwner[_tokenId] = _to;
    Transfer(_from, _to, _tokenId);
  }



チャプター 6: ERC721トランスファーの続きとテストの実行

ここではパブリックのtransfer関数を実装します。

トークン/ゾンビの所有者だけがそれを移転できるよう確認したい。所有者だけに関数へアクセスできるよう制限するやり方を覚えているだろうか?そうだ、お主が覚えているようにonlyOwnerOf修飾詞がそれを行う。この関数に追加しよう。

function transfer(address _to, uint256 _tokenId) public onlyOwnerOf(_tokenId) {
    // 2. ここで関数を定義せよ
  }

②今度は関数の中身についてだが、1行でなくてはならない...ただ_transferを呼び出すだけだ。address _fromの引数にmsg.senderが渡されているようにすること。

function transfer(address _to, uint256 _tokenId) public onlyOwnerOf(_tokenId) {
    _transfer(msg.sender, _to, _tokenId);
  }


お疲れさまでした!


<参考>
CryptoZombies - イーサリアム上でゲームを開発する方法を学習。Powered by Loom Network

【CryptZombies】レッスン5チャプター4:リファクタリング

リファクタリング

リファクタリングとは、プログラムの外部から見た動作を変えずにソースコードの内部構造を整理することです。

前チャプターで、ownerOfという関数を定義しました。
しかし、レッスン4を振り返ると、同じownerOfという名前のmodifierもzombiefeeding.solの中に作っています。
このコードをコンパイルしようとしても、コンパイラは同じ名前の修飾詞と関数を持つことはできないとエラーを出します。

ERC721トークン規格を使用するということは、定義されたある名前の関数を私たちのコントラクトが持つことを、他のコントラクトから予期されるということになります。
したがって、modifier名を別のものに変更して、レッスン4のコードをリファクタリングしなくてはなりませn。

テストの実行

zombiefeeding.solへ戻ろう。modifier名をownerOfからonlyOwnerOfへ変更していくぞ。

①修飾詞名の定義をonlyOwnerOfに変更せよ。

modifier onlyownerOf(uint _zombieId) {
    require(msg.sender == zombieToOwner[_zombieId]);
    _;
  }

②下へスクロールしていき、この修飾詞を使っているfeedAndMultiply関数を見つけたら、ここでも同じように名前を変更せよ。

 function feedAndMultiply(uint _zombieId, uint _targetDna, string _species) internal onlyownerOf(_zombieId) {
    中略
  }


お疲れさまでした!


<参考>
CryptoZombies - イーサリアム上でゲームを開発する方法を学習。Powered by Loom Network

【CryptZombies】レッスン5チャプター3:balanceOfとownerOf

balanceOf

function balanceOf(address _owner) public view returns (uint256 _balance);

この関数は単にaddressを受け取り、そのaddressのトーク保有量を返す。
(ここでは、『トークン』はゾンビになります)

ownerOf

  function ownerOf(uint256 _tokenId) public view returns (address _owner);

この関数はトークンID (この場合ゾンビのID)を受け取り、その所有者の addressを返す。

このDAppにはすでにこの情報を格納している mappingがあるので、実装はとても簡単です。
この関数はreturnステートメントの1行で実装できます。

テストの実行

さあテストだ
これら2つの関数の実装方法を理解しよう。
各関数はシンプルにreturnステートメントのコード1行だけにするのだ。このデータをどこで保存しているか、前のレッスンを見てみるといいぞ。もしわからなければ、『答えを見る』ボタンでヒントを得ても良いからな。

①balanceOfを実装し、_ownerのゾンビ保有数を返すようにせよ。

zombiefactory.solで作成したmappingで、onwerZombieCountは、アドレスからゾンビ保有数を導き出すものでした。

return ownerZombieCount[_owner];

②ownerOfを実装し、_tokenIdをIDに持つゾンビを保有する者のアドレスを返せ。

zombiefactory.solで作成したmappingで、zombieToOwnerは、ゾンビIdから所有者のアドレスを導き出すものでした。

return zombieToOwner[_tokenId];


お疲れさまでした!


<参考>
CryptoZombies - イーサリアム上でゲームを開発する方法を学習。Powered by Loom Network

【CryptZombies】レッスン5チャプター2:ERC721規格と多重継承

ERC721規格は次のようになっています。実装が必要なメソッドのリストとも言えます。

contract ERC721 {
  event Transfer(address indexed _from, address indexed _to, uint256 _tokenId);
  event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);

  function balanceOf(address _owner) public view returns (uint256 _balance);
  function ownerOf(uint256 _tokenId) public view returns (address _owner);
  function transfer(address _to, uint256 _tokenId) public;
  function approve(address _to, uint256 _tokenId) public;
  function takeOwnership(uint256 _tokenId) public;
}

注: ERC721規格は現在 ドラフト であり、まだ正式に合意された実装ではありません。ここではOpenZeppelinのライブラリの現バージョンを使用していますが、将来正式な規格リリース前に変更される可能性があります。この実装については可能なものの ひとつ であり、ERC721規格の正式なものとは考えないでください。

トークン・コントラクトの実装

トークン・コントラクト実装の際、初めにすべきはインターフェースをSolidityファイルにコピー/インポートすることです( import "./erc721.sol";)。
するとコントラクトはそれを継承し、関数定義で各メソッドをオーバーライドします。
Solidityでは、コントラクトは次のように複数コントラクトを継承可能です:

contract SatoshiNakamoto is NickSzabo, HalFinney {
  }

複数の継承をするときは、継承している複数のコントラクトをコンマ( ,)で区切るだけです。
この場合、SatoshiNakamotoはNickSzaboとHalFinneyを継承しています。

テストの実行

①erc721.sol を zombieownership.solにインポートせよ。

import "./erc721.sol";

②ZombieOwnershipがZombieAttackとERC721を継承することを宣言せよ。

contract ZombieOwnership is ZombieAttack, ERC721 {
}


お疲れさまでした!


<参考>
CryptoZombies - イーサリアム上でゲームを開発する方法を学習。Powered by Loom Network

【CryptZombies】レッスン5チャプター1:イーサリアム上トークン

トーク

イーサリアム上のトーク は、基本的にいくつかの共通ルールに従ったスマート・コントラクトのことです。
具体的に言うと、transfer(address _to, uint256 _value) や balanceOf(address _owner) といった関数のスタンダードセットを実装しているものです。
通常スマートコントラクトは、内部に各アドレスにどれだけの残高があるかを記録する mapping(address => uint256) balancesを持っています。
つまり基本的にはトークとは、誰がトークンをどれくらいを所有しているのかを記録するコントラクトと、ユーザーが自分のトークンを他のアドレスに送ることができるようにする機能のことです。

なぜトークンが重要なのか?

ERC20トークは同じ名前の同じ関数セットを共用しているので、同じ方法で相互に作用することが可能となっています。
つまり、とあるERC20トークンとやりとりするアプリケーションを作った場合、他のERC20トークンとやりとりすることも可能なのです。こうすることでカスタムコーディングをせずとも、将来もっと多くのトークンをアプリに追加することができます。ただ新しいトークンのコントラクト・アドレスを入力するだけで、もうアプリは別のトークンを使えるようになるのです。
この一例として挙げられるのは取引所です。取引所が新たなERC20トークンを追加するのに必要なのは、トークンとやり取りするためのスマート・コントラクトをただ追加することです。ユーザーはトークンを取引所のウォレットアドレスに送るようコントラクトに指示でき、取引所はユーザーが引き出しを要求した場合にトークンを彼らに送り返すようコントラクトに指示することができます。
取引所はこのトランスファー・ロジックを一度実装すれば、新たにERC20トークンを追加したい場合に、新しいコントラクト・アドレスをデータベースに追加するだけで良いということです。

別のトークン規格

ERC20トークンは、通貨のような働きをする非常に素晴らしいトークンです。 ただし、この企画でゾンビ・ゲーム内のゾンビを表すのは非常に不便です。
理由としてまず、ゾンビは通貨のように分けることができないことが挙げられます。例えば0.237ETHを送ることはできても、0.237のゾンビを送ることはできません。
次に、全てのゾンビが同じように出来ていないということもその理由です。
例えば、レベル2のゾンビ"Steve"は、相手のレベル732のゾンビ"H4XF13LD MORRIS 💯💯😎💯💯"とは全く別物です。
CryptoZombiesのようなクリプト収集物により適したトークン規格として、ERC721トークンがあります。
ERC721トーク は、それぞれがユニークであると仮定され、分割出来ないので 相互に交換可能できません 。一つの単位ごとの取引のみ可能で、それぞれが特有のIdを持っています。なので、ゾンビをトレード可能にするのに完璧に適したものなのです。

注: ERC721のような標準を使用するメリットとして、オークション及びプレイヤーがゾンビをトレード/販売するやり方を決定するエスクロー・ロジックをコントラクト内で実装する必要がなくなるという点があります。仕様に準拠すれば、他の誰かが収集可能なERC721クリプト資産の交換プラットフォームを作ることができ、私たちのERC721ゾンビはそのプラットフォームで使用できるようになります。そのため、独自の取引ロジックを展開する代わりにトークン規格を使用するのは明らかにメリットがあります。

テストの実行

次のチャプターでERC721の実装をやっていくが、まずはこのレッスン向けにファイル構成をセットアップしていくぞ。
ZombieOwnershipというコントラクトに全てのERC721メソッドを格納していこう。

①pragmaのバージョンをファイルの冒頭で宣言せよ。(シンタックスは前のレッスンでやったファイルをチェックすること)

pragma solidity ^0.4.19;

②このファイルにzombieattack.solをimportすること。

同じディレクトリ(./)にあるzombieattack.solをインポートします。

import "./zombieattack.sol";

③ZombieOwnershipという新たなコントラクトを宣言し、ZombieAttackを継承させよ。まだコントラクトの中身は空のままでよい。

contract ZombieOwnership is ZombieAttack {    
}

お疲れさまでした!


<参考>
CryptoZombies - イーサリアム上でゲームを開発する方法を学習。Powered by Loom Network