ERC-721で簡単な図書館システムを作っていること 

今日、ERC-721で簡単な図書館システムを作りました。Ropstenのテストネット上でこのスマートコントラクトを作りました。

## ERC-20 vs. ERC-721トークン基準
ERC-20トークン基準は暗号柄を作るに使っているだけ。ERC-20トークン基準とは異なり、ERC-721トークン基準は様々資産を管理できます。例えば、[CryptoKitties](https://www.cryptokitties.co/)はERC-721トークン基準で資産を管理しています。

## コード
```solidity:libraryContractNFT.sol
pragma solidity ^0.4.25;

import "https://github.com/OpenZeppelin/openzeppelin-solidity/contracts/token/ERC721/ERC721Full.sol";
import "https://github.com/OpenZeppelin/openzeppelin-solidity/contracts/access/roles/MinterRole.sol";

contract LibraryNFT is ERC721Full, MinterRole {
    struct Book {
        uint256 id;
        string title;
        string author;
    }

    Book[] private bookCollection;
    address private _contractCreator;
    mapping(address => uint8) private _borrowedCount;

    constructor() ERC721Full("LibraryNFT", "LIB") public {
        _contractCreator = msg.sender;
    }

    function moveBook(address _from, address _to, uint256 _tokenId) private {
        require(_to != address(0));

        super._clearApproval(_from, _tokenId);
        super._removeTokenFrom(_from, _tokenId);
        super._addTokenTo(_to, _tokenId);

        emit Transfer(_from, _to, _tokenId);
    }

    function addBook(string _title, string _author) public onlyMinter returns (bool) {
        super._mint(_contractCreator, bookCollection.length+1);
        bookCollection.push(Book(bookCollection.length+1, _title, _author));
        return true;
    }

    function borrowBook(uint256 _bookId) public {
        require(_borrowedCount[msg.sender] < 5);
        require(super.ownerOf(_bookId) == _contractCreator);
        moveBook(super.ownerOf(_bookId), msg.sender, _bookId);
        _borrowedCount[msg.sender]++;
    }

    function returnBook(uint256 _bookId) public {
        moveBook(msg.sender, _contractCreator, _bookId);
        _borrowedCount[msg.sender]--;
    }
    
    function getBookTitle(uint256 _bookId) public view returns (string) {
        return bookCollection[_bookId-1].title; 
    }
    
    function getBookAuthor(uint256 _bookId) public view returns (string) {
        return bookCollection[_bookId-1].author; 
    }
}
```
このコードは単純な図書館システム。本を付け加えれて借りれて返​せ​るにできます。

### スタート
```solidity:libraryContractNFT.sol
pragma solidity ^0.4.25;

import "https://github.com/OpenZeppelin/openzeppelin-solidity/contracts/token/ERC721/ERC721Full.sol";
import "https://github.com/OpenZeppelin/openzeppelin-solidity/contracts/access/roles/MinterRole.sol";
```
最初の行にSolidityのバージョンを入れます。次、we import OpenZeppelin's ERC-721 Token implementation, and MinterRole to determine who is able to mint new tokens. In this case, this is equivalent to adding new books.

### Constructor関数
```solidity:libraryContractNFT.sol
contract LibraryNFT is ERC721Full, MinterRole {
    struct Book {
        uint256 id;
        string title;
        string author;
    }

    Book[] private bookCollection;
    address private _contractCreator;
    mapping(address => uint8) private _borrowedCount;

    constructor() ERC721Full("LibraryNFT", "LIB") public {
        _contractCreator = msg.sender;
    }
    ...
}
```
このスマートコントラクトはOpenZeppelinのERC-720 Token Implementationに相続しています。

Constructor関数に様々な価値を定義しています。例えば、トークンの名前や記号です。I am also set as the contractCreator, where all books will be stored if not borrowed by anyone.

### 基本的な関数
```solidity:libraryContractNFT.sol
pragma solidity ^0.4.25;

import "https://github.com/OpenZeppelin/openzeppelin-solidity/contracts/token/ERC721/ERC721Full.sol";
import "https://github.com/OpenZeppelin/openzeppelin-solidity/contracts/access/roles/MinterRole.sol";

contract LibraryNFT is ERC721Full, MinterRole {
    ...
    function moveBook(address _from, address _to, uint256 _tokenId) private {
        require(_to != address(0));

        super._clearApproval(_from, _tokenId);
        super._removeTokenFrom(_from, _tokenId);
        super._addTokenTo(_to, _tokenId);

        emit Transfer(_from, _to, _tokenId);
    }

    function addBook(string _title, string _author) public onlyMinter returns (bool) {
        super._mint(_contractCreator, bookCollection.length+1);
        bookCollection.push(Book(bookCollection.length+1, _title, _author));
        return true;
    }

    function borrowBook(uint256 _bookId) public {
        require(_borrowedCount[msg.sender] < 5);
        require(super.ownerOf(_bookId) == _contractCreator);
        moveBook(super.ownerOf(_bookId), msg.sender, _bookId);
        _borrowedCount[msg.sender]++;
    }

    function returnBook(uint256 _bookId) public {
        moveBook(msg.sender, _contractCreator, _bookId);
        _borrowedCount[msg.sender]--;
    }
}
```
Here are the required functions for the 図書館システム. First, I made my own implementation of transferTo, because I had to disregard the owner of the book. Then, we have functions to enable the adding, borrowing, and returning of books. A user is limited to borrowing only up to 5 books.

### 補足
```solidity:libraryContractNFT.sol
contract LibraryNFT is ERC721Full, MinterRole {
    ...
    function getBookTitle(uint256 _bookId) public view returns (string) {
        return bookCollection[_bookId-1].title; 
    }
    
    function getBookAuthor(uint256 _bookId) public view returns (string) {
        return bookCollection[_bookId-1].author; 
    }
}
```
この補足の関数は「本の作家はだれですか？」と「本のタイトルは何ですか？」という質問に答えるものです。

Contract Address (__0x21d069a5c270fa892897a2b9ad4be6d24f989ebe__)にこのスマートコントラクトを見れます。

もっと読む：

* [ERC-20 Token Standard](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md)
* [ERC-721 Non-Fungible Token Standard](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md)
* [OpenZeppelin GitHub repository](https://github.com/OpenZeppelin/openzeppelin-solidity/)