Help us understand the problem. What is going on with this article?

ライブラリシステム with ERC721

More than 1 year has passed since last update.

Today, I will be showing how I created a library system with ERC721.

A point to notice: This is the same library system that I created in one of my previous articles, I just integrated it with ERC721.

LibrarySystemWithERC721
pragma solidity ^0.4.18;

import "https://github.com/OpenZeppelin/openzeppelin-solidity/contracts/token/ERC721/ERC721Metadata.sol";

contract LibrarySystem is ERC721Metadata{

  struct Book {
    string author;
    string bookName;
    bool borrowed;
    address ownerAddress;
    address currentHolder;
  }

  struct Transaction{
    uint bookId;
    address holderAddress;
  }

  mapping (uint => Book) private books;
  mapping (uint => Transaction) private transactions;

  uint private bookCounter;
  uint private transactionCounter;

  address private creator;

  constructor() public ERC721Metadata("Markus Xamuel Viola", "MXV"){
    creator = msg.sender;   
    bookCounter = 0;
    transactionCounter = 0;
  }

  function addBook(string author, string bookName) public {
    bookCounter++;
    books[bookCounter] = Book(author, bookName, false, msg.sender, 0);
    super._mint(msg.sender, bookCounter);
  }

  function returnBook(uint index) public {
    require(books[index].borrowed == true && 
            (msg.sender == books[index].currentHolder || msg.sender == books[index].ownerAddress));

    super.transferFrom(books[index].currentHolder, books[index].ownerAddress, index); 
    books[index].borrowed = false;

    //To also transact that the book was returned in the records.
    transactionCounter++;
    transactions[transactionCounter] = Transaction(index, msg.sender);
  }

  function borrowBook(uint _bookId) public {
    require(books[_bookId].borrowed == false && 
            bookCounter != 0 &&
            compareStringsByBytes(books[_bookId].bookName, "") == false);

    super.transferFrom(books[_bookId].ownerAddress, msg.sender, _bookId);
    books[_bookId].borrowed = true;
  }

  function isBookBorrowed(uint index) public view returns(bool){
    require(compareStringsByBytes(books[index].bookName, "") == false);
    return books[index].borrowed;
  }

  function viewTransactionById(uint index) public view returns(string bookName, address accountId){
    string memory _bookName = books[transactions[index].bookId].bookName;
    return (_bookName, transactions[index].holderAddress);
  }

  function compareStringsByBytes(string string1, string string2) public pure returns(bool){
    bytes memory _s1 = bytes(string1);
    bytes memory _s2 = bytes(string2);
    return keccak256(_s1) == keccak256(_s2);
  }

}

Variables & Inheritance

In the contract, I extended ERC721MetaData since it is an ERC721 Standard Contract with extra features like the name, symbol and token URI. This library will help us establish our own ERC721 integrated program.

I created two structs, the transaction and the book:

The book has some necessary information like the author, book name, and the borrowed variable to check if the book is borrowed or not. It also has the owner address and the current holder address, to track who owns and who borrows the book.

The transaction struct however, is used to freely see and record the transaction of borrowing. It will track whether the book was returned or not, and who is currently holding it.

contract LibrarySystem is ERC721Metadata{

  struct Book {
    string author;
    string bookName;
    bool borrowed;
    address ownerAddress;
    address currentHolder;
  }

  struct Transaction{
    uint bookId;
    address holderAddress;
  }

  mapping (uint => Book) private books;
  mapping (uint => Transaction) private transactions;

  uint private bookCounter;
  uint private transactionCounter;

  ...
}

Construction & Initializations

In this section, we set the name and the symbol of our token using the constructor of ERC721Metadata. We also set the creator's address, and setting the values of the counters for the book struct and transaction struct to 0.

  address private creator;

  constructor() public ERC721Metadata("Markus Xamuel Viola", "MXV"){
    creator = msg.sender;   
    bookCounter = 0;
    transactionCounter = 0;
  }

Adding a Book

In adding a book, we mint a new token to set the identity of the book, giving the sender's address and setting the token ID for the book.

  function addBook(string author, string bookName) public {
    bookCounter++;
    books[bookCounter] = Book(author, bookName, false, msg.sender, 0);
    super._mint(msg.sender, bookCounter);
  }

Returning a Book

When returning a book, there can only be two conditions to return it, the book should be actually borrowed, the borrower and the owner are the only who can return the book.

In returning a book, it will be recorded in the transaction. And most importantly, it will put back the ownership to the original book owner.

  function returnBook(uint index) public {
    require(books[index].borrowed == true && 
            (msg.sender == books[index].currentHolder || msg.sender == books[index].ownerAddress));

    super.transferFrom(books[index].currentHolder, books[index].ownerAddress, index); 
    books[index].borrowed = false;

    //To also transact that the book was returned in the records.
    transactionCounter++;
    transactions[transactionCounter] = Transaction(index, msg.sender);
  }

Borrowing a Book

In borrowing a book we just simply check if the book is not borrowed, then we transfer the ownership to the sender who is borrowing it. It will also set the book's borrowing status to false.

  function borrowBook(uint _bookId) public {
    require(books[_bookId].borrowed == false && 
            bookCounter != 0 &&
            compareStringsByBytes(books[_bookId].bookName, "") == false);

    super.transferFrom(books[_bookId].ownerAddress, msg.sender, _bookId);
    books[_bookId].borrowed = true;
  }

  function isBookBorrowed(uint index) public view returns(bool){
    require(compareStringsByBytes(books[index].bookName, "") == false);
    return books[index].borrowed;
  }

  function viewTransactionById(uint index) public view returns(string bookName, address accountId){
    string memory _bookName = books[transactions[index].bookId].bookName;
    return (_bookName, transactions[index].holderAddress);
  }

  function compareStringsByBytes(string string1, string string2) public pure returns(bool){
    bytes memory _s1 = bytes(string1);
    bytes memory _s2 = bytes(string2);
    return keccak256(_s1) == keccak256(_s2);
  }

}

Other Functions

The other functions are basically what a library system should do, the 3 functions however, showcases and used the ERC721 for transfering and returning ownership.

markusveeyola
こんにちは、マルクスと言って、日本語の学生で、同時に、ITエンジニアとして、アルバイトしています。主にKotlinとC#言語を得意としています。自分自身を理解しているように、新しい学ぶことが好きです。特にIT技術に関して、やりたい事があれば、絶対に集中して、絶え間なくします。学び続けて頑張ります!
https://www.twitter.com/markusveeyola
bit-okutama
外国人ITエンジニア育成を目的とした日本語学校です
http://bit-okutama.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした