1
0

More than 5 years have 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.

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0