//tips
//smart contract
勢いがついたのでこのままクリプトゾンビ攻略を進めてしまう。
ERC721の所有権移転には2パターンある。
・transfer関数での移転
→トークンの送り手が関数を呼び出すケース
・takeOwnershipでの移転
→トークンの受け手が関数を呼び出すケースでapprove関数での承認手続きが入る分複雑になる
ただ、ベースとなる作業は同じで下記なのでこちらをprivateとし、外部からアクセスするものを別途2パターンに分けて作成する。
function _transfer(address _from, address _to, uint256 _tokenId) private {
ownerZombieCount[_to]++;
ownerZombieCount[_from]--;
zombieToOwner[_tokenId] = _to;
Transfer(_from, _to, _tokenId);
}
こちらが一つ目のトークンの送り手が呼び出すタイプ。
function transfer(address _to, uint256 _tokenId) public onlyOwnerOf(_tokenId) {
_transfer(msg.sender, _to, _tokenId);
}
2つ目は、トークン所有者がapprove関数を呼び出し、新所有者のaddress、_tokenIdの情報を与え、新所有者がtakeOwnership関数を_tokenIdで呼び出すと、コントラクトがapprove関数から承認済みかを確認してから、トークンを移転する構造を加える。
やっとapproveの意味合いについて理解できてきたかもしれない。プラットフォームだけが要求するものではない。
まずは新たに承認データベースを設定。
mapping (uint => address) zombieApprovals;
そこから先のベースにつなぐ関数を実装。
function transfer(address _to, uint256 _tokenId) public onlyOwnerOf(_tokenId) {
_transfer(msg.sender, _to, _tokenId);
}
function approve(address _to, uint256 _tokenId) public onlyOwnerOf(_tokenId) {
zombieApprovals[_tokenId] = _to;
Approval(msg.sender, _to, _tokenId);
}
function takeOwnership(uint256 _tokenId) public {
require(zombieApprovals[_tokenId] == msg.sender);
address owner = ownerOf(_tokenId);
_taransfer(owner, msg.sender , _tokenId)
}
結局は下記の_transferで所有の移転が行われることには変わりはない。
ownerZombieCount[_to]++;
ownerZombieCount[_from]--;
zombieToOwner[_tokenId] = _to;
コントラクトのセキュリティのポイントとなるオーバーフローとアンダーフローの回避の確認。
オーバーフロー は、定められた桁数を超過した場合のことで例えば、uint8は8ビットであるため2^8 - 1 = 255が最大値となるが、number++;などと関数を書いていると超過する場合がある。
具体的には255を表す、バイナリの11111111に1を足すと、00000000に戻る。つまり255+1は0になる。
逆にアンダーフローは0から1を引くと、255となる。
これらの問題を回避するために、OpenZeppelinはSafeMathというライブラリ用意してくれているので、こちらを標準装備しておくとよさそう。
ライブラリとは、特別なタイプのコントラクトで、データ型への関数アタッチができる。例えば、uintで定義されたものに直接関数を付け加えられることが可能となる。uint256 b = a.add(3);
.add(3)の部分が機能として追加できると考えればわかりやすいか。
少し特殊なのはコントラクトの後にusingを使用する必要があること。
contract ZombieFactory is Ownable {
using SafeMath for uint256;