[ PizzaCoin the Series #6 ] Integrating PizzaCoin Contract with Dependencies

October 23, 2018
Ethereum Solidity Smart Contract Blockchain Pizza Hackathon Pizza Coin

Figure 1. Pizza Hackathon 2018


Welcome to the final part of PizzaCoin the Series. In the previous article, you have learned how PizzaCoin contract deploys its children contracts using a contract-factory pattern. In this article, you will learn how PizzaCoin contract was implemented in order to integrate and interact with its dependency components.

Terms used in this article


PizzaCoin – the mother contract of PizzaCoinStaff, PizzaCoinPlayer and PizzaCoinTeam contracts.

PizzaCoinStaff – one of the three PizzaCoin’s children contracts responsible for managing staff-related tasks such as registering staffs, revoking staffs, providing staff information, and managing token balance as well as voting action for a staff.

PizzaCoinPlayer – one of the three PizzaCoin’s children contracts responsible for managing player-related tasks such as registering players, revoking players, providing player information, and managing token balance as well as voting action for a player.

PizzaCoinTeam – one of the three PizzaCoin’s children contracts responsible for managing team-related tasks such as creating teams, registering a player to a specific team, revoking teams, revoking a specific player from a particular team, handling team voting, and providing team information as well as voting results.

PizzaCoinStaffDeployer – a contract factory library for deploying PizzaCoinStaff contract.

PizzaCoinPlayerDeployer – a contract factory library for deploying PizzaCoinPlayer contract.

PizzaCoinTeamDeployer – a contract factory library for deploying PizzaCoinTeam contract.

PizzaCoinCodeLib – one of the two proxy libraries used by PizzaCoin contract.

PizzaCoinCodeLib2 – one of the two proxy libraries used by PizzaCoin contract.

Project Deployer – a user who deploys PizzaCoin contract which is considered as one of staffs.

Source files refered to in this article


PizzaCoin.sol

PizzaCoinCodeLib.sol

PizzaCoinCodeLib2.sol

PizzaCoinPlayerDeployer.sol

ERC20.sol

2_deploy_contracts.js

Deployment of PizzaCoin contract



pragma solidity ^0.4.23;

import "./ERC20.sol";
import "./BasicStringUtils.sol";
import "./Owned.sol";
import "./PizzaCoinStaff.sol";
import "./PizzaCoinPlayer.sol";
import "./PizzaCoinTeam.sol";
import "./PizzaCoinStaffDeployer.sol";
import "./PizzaCoinPlayerDeployer.sol";
import "./PizzaCoinTeamDeployer.sol";
import "./PizzaCoinCodeLib.sol";
import "./PizzaCoinCodeLib2.sol";


// ----------------------------------------------------------------------------
// Pizza Coin Contract
// ----------------------------------------------------------------------------
contract PizzaCoin is ERC20, Owned {
    using BasicStringUtils for string;


    // Contract events (the 'indexed' keyword cannot be used with any string parameter)
    event StateChanged();
    event ChildContractCreated(address indexed _contract);
    event StaffRegistered();
    event StaffKicked();
    event PlayerRegistered();
    event TeamCreated();
    event PlayerKicked();
    event TeamKicked();
    event TeamVoted(string _teamName, uint256 _totalVoted);


    // Token info
    string public constant symbol = "PZC";
    string public constant name = "Pizza Coin";
    uint8 public constant decimals = 0;

    string private ownerName;
    uint256 private voterInitialTokens;

    address private staffContract;
    address private playerContract;
    address private teamContract;

    enum State { Initial, Registration, RegistrationLocked, Voting, VotingFinished }
    State private state = State.Initial;

    // mapping(keccak256(state) => stateInString)
    mapping(bytes32 => string) private stateMap;


    // ------------------------------------------------------------------------
    // Constructor
    // ------------------------------------------------------------------------
    constructor(string _ownerName, uint256 _voterInitialTokens) public {
        require(
            _ownerName.isNotEmpty(),
            "'_ownerName' might not be empty."
        );

        require(
            _voterInitialTokens > 0,
            "'_voterInitialTokens' must be larger than 0."
        );

        initStateMap();

        ownerName = _ownerName;
        voterInitialTokens = _voterInitialTokens;

        emit StateChanged();
    }



    ...
    ...
    ...
}

Code Snippet 1. Excerpt from PizzaCoin contract’s source code


Code snippet 1 shows some excerpt from PizzaCoin contract’s source code. In the line no’s. 3 - 13, all dependency source files are imported. In the line no. 19, PizzaCoin contract inherits from Owned contract, which you can find out more about the discussion on it in detail in the previous article. Furthermore, the contract also inherits from ERC-20 token standard contract. We will separate out the discussion on ERC-20 implementation into the last section of this article.

In the line no’s. 24 - 32, the contract defines a group of contract events. Events provide logging facilities which enable DApp or backend developer directly read logging messages in the past or subscribe to any specific event to get notified from a contract in real time. You will better understand the benefits of using events after you finish reading this article.

In the line no’s. 36 - 51, a bunch of state variables used under PizzaCoin contract are defined. Most of the variables are self-explanatory. Though there are three interesting variables needed to be emphasized here, namely, staffContract, playerContract and teamContract which are of type address as defined in the line no’s. 43 - 45. These variables will be used to store addresses of deployed children contracts and we will use these variables when communicating with those children contracts. In the line no’s. 57 - 74, PizzaCoin contract’s constructor is defined. The constructor requires a project deployer’s name and initial voting tokens predetermined by the deployer as input arguments.

Figure 2. Deployment of PizzaCoin contract



var PizzaCoin = artifacts.require("./PizzaCoin.sol");
var PizzaCoinStaffDeployer = artifacts.require("./PizzaCoinStaffDeployer.sol");
var PizzaCoinPlayerDeployer = artifacts.require("./PizzaCoinPlayerDeployer.sol");
var PizzaCoinTeamDeployer = artifacts.require("./PizzaCoinTeamDeployer.sol");
var PizzaCoinCodeLib = artifacts.require("./PizzaCoinCodeLib.sol");
var PizzaCoinCodeLib2 = artifacts.require("./PizzaCoinCodeLib2.sol");

module.exports = function(deployer) {
    deployer.deploy(PizzaCoinStaffDeployer);
    deployer.deploy(PizzaCoinPlayerDeployer);
    deployer.deploy(PizzaCoinTeamDeployer);
    deployer.deploy(PizzaCoinCodeLib);
    deployer.deploy(PizzaCoinCodeLib2);

    deployer.link(PizzaCoinStaffDeployer, PizzaCoin);
    deployer.link(PizzaCoinPlayerDeployer, PizzaCoin);
    deployer.link(PizzaCoinTeamDeployer, PizzaCoin);
    deployer.link(PizzaCoinCodeLib, PizzaCoin);
    deployer.link(PizzaCoinCodeLib2, PizzaCoin);

    deployer.deploy(PizzaCoin, "Phuwanai Thummavet", 3);
};

Code Snippet 2. Truffle contract deployment configuration


PizzaCoin contract deployment diagram is illustrated in Figure 2. In our project, we adopted Truffle framework to facilitate us when deploying contracts. Truffle configuration for deploying PizzaCoin contract and its dependencies is showned in the code snippet 2.

Firstly, the dependency libraries, including PizzaCoinStaffDeployer, PizzaCoinPlayerDeployer, PizzaCoinTeamDeployer, PizzaCoinCodeLib and PizzaCoinCodeLib2, have to be compiled into separate EVM bytecodes. Secondly, all of the dependency bytecodes have to be deployed onto the blockchain one by one as separate transactions according to the line no’s. 9 - 13 of the snippet 2. Thirdly, the previously deployed libraries’ addresses would then be linked and injected as dependency instances in order to build the bytecode of PizzaCoin mother contract in the line no’s. 15 - 19. Lastly, the bytecode of PizzaCoin contract would be deployed onto the blockchain in the line no. 21. Note that, the second and the third arguments in the line no. 21 are the values passed to PizzaCoin contract’s constructor defined in the line no. 57 of the snippet 1.

Since we injected PizzaCoinStaffDeployer, PizzaCoinPlayerDeployer, PizzaCoinTeamDeployer, PizzaCoinCodeLib and PizzaCoinCodeLib2 library instances into the EVM bytecode of PizzaCoin contract, the injected libraries will be visible to PizzaCoin contract. Thus, PizzaCoin contract is able to refer to any of those libraries like this example statement: PizzaCoinCodeLib.registerStaff( ...args...).

Initialization of PizzaCoin contract


Figure 3. Initialization of PizzaCoin contract


Even though PizzaCoin contract has already been deployed, its children contracts are not yet deployed. Actually, the initialization of PizzaCoin contract has already been discussed in the previous article. Nevertheless, let’s quickly recap once again. PizzaCoin contract leverages a contract-factory pattern to deploy its children contracts. As you can see in Figure 3, a project deployer initiates three separate transactions to PizzaCoin contract (steps 1.1, 2.1 and 3.1) by invoking createStaffContract, createPlayerContract and createTeamContract functions in any order.

Then, each invoked function makes a call to deployContract function on the corresponding deployer library (steps 1.2, 2.2 and 3.2). Each deployer then deploys the corresponding child contract onto the blockchain (steps 1.3, 2.3 and 3.3). The resulting children contract instances would then be returned from the deployers to store on PizzaCoin contract (steps 1.4, 2.4 and 3.4). With the obtained instances, PizzaCoin contract has an approach to communicating with its children contracts. For the sake of understanding, let’s deep dive into the code.


// ------------------------------------------------------------------------
// Create a player contract
// ------------------------------------------------------------------------
function createPlayerContract() public onlyInitialState onlyOwner returns (address _contract) {
    require(
        playerContract == address(0),
        "The player contract got initialized already."
    );

    // Create a player contract
    playerContract = PizzaCoinPlayerDeployer.deployContract(voterInitialTokens);

    emit ChildContractCreated(playerContract);
    return playerContract;
}

Code Snippet 3. The function createPlayerContract() on PizzaCoin invokes PizzaCoinPlayerDeployer in order to deploy PizzaCoinPlayer


Code snippet 3 describes the implementation of createPlayerContract function on PizzaCoin contract. Let’s say a project deployer would like to deploy PizzaCoinPlayer contract. The deployer makes a call to createPlayerContract function on PizzaCoin contract. The function would verify if PizzaCoinPlayer contract does not get deployed yet in the line no’s. 5 - 8. The transaction would be reverted if PizzaCoinPlayer contract get deployed already in the line no. 7. If it isn’t so, the function communicates with PizzaCoinPlayerDeployer library in order to deploy PizzaCoinPlayer contract with the statement PizzaCoinPlayerDeployer. deployContract(voterInitialTokens) in the line no. 11.

The implementation of deployContract function on PizzaCoinPlayerDeployer library is expressed in the code snippet 4 below. The function would deploy PizzaCoinPlayer contract with the new operator in the line no. 25 and the function would return an address of the deployed contract to PizzaCoin contract if everything went fine. Consequently, the returned address would be stored in the variable playerContract (which is a global state variable previously declared in the line no. 44 of the snippet 1) in the line no. 11 of the snippet 3. Lastly, createPlayerContract function emits the event ChildContractCreated along with returning of the last received address playerContract to the project deployer in the line no’s. 13 and 14.


pragma solidity ^0.4.23;

import "./PizzaCoinPlayer.sol";


// ----------------------------------------------------------------------------
// Pizza Coin Player Deployer Library
// ----------------------------------------------------------------------------
library PizzaCoinPlayerDeployer {

    // ------------------------------------------------------------------------
    // Create a player contract
    // ------------------------------------------------------------------------
    function deployContract(uint256 _voterInitialTokens) 
        public
        returns (
            PizzaCoinPlayer _playerContract
        ) 
    {
        require(
            _voterInitialTokens > 0,
            "'_voterInitialTokens' must be larger than 0."
        );

        _playerContract = new PizzaCoinPlayer(_voterInitialTokens);
    }
}

Code Snippet 4. PizzaCoinPlayerDeployer is a contract factory for PizzaCoinPlayer


Interaction among deployed contracts


Figure 4. PizzaCoin contract acts as a contract coordinator for PizzaCoinStaff, PizzaCoinPlayer and PizzaCoinTeam contracts


Once PizzaCoin contract and its depencencies get deployed successfully, PizzaCoin contract would act as a coordinator for its children contracts like what is depicted in Figure 4 above. Let’s get into the code to understand this better.


// ------------------------------------------------------------------------
// Register a new staff
// ------------------------------------------------------------------------
function registerStaff(address _newStaff, string _newStaffName) 
    public onlyRegistrationState onlyStaff notRegistered(_newStaff) 
{
    PizzaCoinCodeLib.registerStaff(_newStaff, _newStaffName, staffContract);
    emit StaffRegistered();
}

Code Snippet 5. The use of PizzaCoinCodeLib as a proxy library for communicating with PizzaCoinStaff contract


Let’s say a staff invokes registerStaff function on PizzaCoin contract in order to register some new staff. The function is as very simple as defined in the line no. 7 of the snippet 5. It just hands over the process to the delegated function which is on PizzaCoinCodeLib library. That is, the function passes three arguments to its delegated function like the following statement PizzaCoinCodeLib.registerStaff( _newStaff, _newStaffName, staffContract). Interestingly, the instance of PizzaCoinStaff contract named staffContract is passed as the third argument.


pragma solidity ^0.4.23;

import "./BasicStringUtils.sol";
import "./PizzaCoinStaff.sol";
import "./PizzaCoinPlayer.sol";
import "./PizzaCoinTeam.sol";


// ----------------------------------------------------------------------------
// Pizza Coin Code Library #1
// ----------------------------------------------------------------------------
library PizzaCoinCodeLib {
    using BasicStringUtils for string;


    // ------------------------------------------------------------------------
    // Register a new staff
    // ------------------------------------------------------------------------
    function registerStaff(address _staff, string _staffName, address _staffContract) public {
        assert(_staffContract != address(0));

        // Get a contract instance from the deployed address
        IStaffContract staffContractInstance = IStaffContract(_staffContract);

        staffContractInstance.registerStaff(_staff, _staffName);
    }



    ...
    ...
    ...
}

Code Snippet 6. The delegated function registerStaff() on PizzaCoinCodeLib library


The implementation of the delegated function registerStaff on PizzaCoinCodeLib library is shown in the line no’s. 19 - 26 of the snippet 6. To process the transaction request, the delegated function converts the third function paramater _staffContract which is of type address to the contract instance variable staffContractInstance of type IStaffContract interface in the line no. 23. Later, the delegated function executes the real worker function which is on PizzaCoinStaff contract with the statement staffContractInstance.registerStaff( _staff, _staffName) in the line no. 25.

One point to notice is that registerStaff function of PizzaCoin mother contract is applied with three modifiers. The three modifiers include onlyRegistrationState, onlyStaff and notRegistered(_newStaff) as defined in the line no. 5 of the snippet 5. This means that the transaction request would be verified before the function invokes its delegated function. The transaction, therefore, would be reverted if it does not satisfy any of the three modifiers.


// ------------------------------------------------------------------------
// Allow any staff or any player vote to a favourite team
// ------------------------------------------------------------------------
function voteTeam(string _teamName, uint256 _votingWeight) public onlyVotingState onlyRegistered {
    uint256 totalVoted = PizzaCoinCodeLib.voteTeam(
        _teamName, 
        _votingWeight, 
        staffContract, 
        playerContract, 
        teamContract
    );
    emit TeamVoted(_teamName, totalVoted);
}

Code Snippet 7. The implementation of voteTeam() on PizzaCoin contract


Another interesting function to be discussed in this section is voteTeam function on PizzaCoin contract as defined in the snippet 7. This function allows any staff or any player to vote to a favourite team. A staff is freely to give a vote to any team whereas a player can commit to vote to any other different team. Each voter can spend voting tokens according to his/her own balance. In the line no. 5, the function does nothing except forwording the process to its delegated function which is on PizzaCoinCodeLib library and receiving the processing result from the delegated function into the variable totalVoted. Finally, the function returns the processing result to a caller by way of emiting the event TeamVoted(_teamName, totalVoted) in the line no. 12. Note that, the function passes instances of PizzaCoin’s children contracts to the delegated function in the line no’s. 8 - 10.


// ------------------------------------------------------------------------
// Allow any staff or any player vote to a favourite team
// ------------------------------------------------------------------------
function voteTeam(
    string _teamName, 
    uint256 _votingWeight, 
    address _staffContract,
    address _playerContract,
    address _teamContract
) 
    public returns (uint256 _totalVoted) 
{

    assert(_staffContract != address(0));
    assert(_teamContract != address(0));

    // Get contract instances from the deployed addresses
    IStaffContract staffContractInstance = IStaffContract(_staffContract);
    ITeamContract teamContractInstance = ITeamContract(_teamContract);

    require(
        _teamName.isNotEmpty(),
        "'_teamName' might not be empty."
    );

    require(
        _votingWeight > 0,
        "'_votingWeight' must be larger than 0."
    );

    require(
        teamContractInstance.doesTeamExist(_teamName),
        "Cannot find the specified team."
    );

    if (staffContractInstance.isStaff(msg.sender)) {
        // Voter is a staff
        return voteTeamByStaff(_teamName, _votingWeight, _staffContract, _teamContract);
    }
    else {
        // Voter is a player
        return voteTeamByDifferentTeamPlayer(_teamName, _votingWeight, _playerContract, _teamContract);
    }
}

// ------------------------------------------------------------------------
// Vote for a team by a staff
// ------------------------------------------------------------------------
function voteTeamByStaff(
    string _teamName, 
    uint256 _votingWeight,
    address _staffContract,
    address _teamContract
) 
    internal returns (uint256 _totalVoted) 
{

    assert(_staffContract != address(0));
    assert(_teamContract != address(0));

    // Get contract instances from the deployed addresses
    IStaffContract staffContractInstance = IStaffContract(_staffContract);
    ITeamContract teamContractInstance = ITeamContract(_teamContract);

    address voter = msg.sender;
    assert(_teamName.isNotEmpty());
    assert(_votingWeight > 0);
    assert(teamContractInstance.doesTeamExist(_teamName));
    assert(staffContractInstance.isStaff(voter));

    require(
        _votingWeight <= staffContractInstance.getTokenBalance(voter),
        "Insufficient voting balance."
    );

    // Staff commits to vote to the team
    staffContractInstance.commitToVote(voter, _teamName, _votingWeight);
    teamContractInstance.voteToTeam(voter, _teamName, _votingWeight);

    // Get the current voting points of the team
    _totalVoted = teamContractInstance.getVotingPointsOfTeam(_teamName);
}

// ------------------------------------------------------------------------
// Vote for a team by a different team's player
// ------------------------------------------------------------------------
function voteTeamByDifferentTeamPlayer(
    string _teamName, 
    uint256 _votingWeight,
    address _playerContract,
    address _teamContract
) 
    internal returns (uint256 _totalVoted) 
{
    assert(_playerContract != address(0));
    assert(_teamContract != address(0));

    // Get contract instances from the deployed addresses
    IPlayerContract playerContractInstance = IPlayerContract(_playerContract);
    ITeamContract teamContractInstance = ITeamContract(_teamContract);
    
    address voter = msg.sender;
    assert(_teamName.isNotEmpty());
    assert(_votingWeight > 0);
    assert(teamContractInstance.doesTeamExist(_teamName));
    assert(playerContractInstance.isPlayer(voter));

    require(
        playerContractInstance.isPlayerInTeam(voter, _teamName) == false,
        "A player is not permitted to vote to his/her own team."
    );

    require(
        _votingWeight <= playerContractInstance.getTokenBalance(voter),
        "Insufficient voting balance."
    );

    // Player commits to vote to the team
    playerContractInstance.commitToVote(voter, _teamName, _votingWeight);
    teamContractInstance.voteToTeam(voter, _teamName, _votingWeight);

    // Get the current voting points of the team
    _totalVoted = teamContractInstance.getVotingPointsOfTeam(_teamName);
}

Code Snippet 8. The delegated function voteTeam() and its dependency functions voteTeamByStaff() and voteTeamByDifferentTeamPlayer() on PizzaCoinCodeLib library


Code snippet 8 shows the implementation of the delegated function voteTeam and its dependency functions which is on PizzaCoinCodeLib library. For the sake of simplicity, I will emphasize only important implementation points. Upon receiving a request, voteTeam function verifies the presence of the team to be voted _teamName in the line no’s. 31 - 34. After that, the function checks if a voter has the role of a staff or a player by consulting with the function isStaff of PizzaCoinStaff contract in the line no. 36. If a voter has the role of a staff the process will be forwarded to the dependency function named voteTeamByStaff in the line no. 38 else the process will be forwarded to another dependency function named voteTeamByDifferentTeamPlayer in the line no. 42.

The implementation of the dependency function voteTeamByStaff is described in the line no’s. 49 - 82. The function verifies that a voter has sufficient tokens to commit a vote by consulting with the function getTokenBalance of PizzaCoinStaff contract in the line no. 72. If a voter has enough voting balance, the function confirms the voting transaction by executing the statements in line no’s. 77 and 78. Eventually, the function returns the current voting points of the voted team to its caller in the line no. 81.

For another dependency function voteTeamByDifferentTeamPlayer as defined in the line no’s. 87 - 124, this function performs the similar logic like the function voteTeamByStaff does. Nonetheless, this function will only handle the voting transaction sent from a player. The only difference between the two functions is that the function voteTeamByDifferentTeamPlayer has an additional user verification check in the line no’s. 108 - 111 to ensure that a voter does not commit to vote to his/her own team.

The implementation of ERC-20 token standard



pragma solidity ^0.4.23;

// ----------------------------------------------------------------------------
// ERC Token Standard #20 Interface
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md
// ----------------------------------------------------------------------------
contract ERC20 {
    function totalSupply() public view returns (uint256 _totalSupply);
    function balanceOf(address tokenOwner) public view returns (uint256 balance);
    function allowance(address tokenOwner, address spender) public view returns (uint256 remaining);
    function transfer(address to, uint256 tokens) public returns (bool success);
    function approve(address spender, uint256 tokens) public returns (bool success);
    function transferFrom(address from, address to, uint256 tokens) public returns (bool success);

    event Transfer(address indexed from, address indexed to, uint256 tokens);
    event Approval(address indexed tokenOwner, address indexed spender, uint256 tokens);
}

Code Snippet 9. ERC-20 token standard


The last section to be discussed in this article is about the implementation of ERC-20 token standard. The abstract contract ERC20 is defined like the code snippet 9. Our PizzaCoin voting system inherits from ERC20 contract in the line no. 4 of the below code snippet 10. In the line no’s. 7 - 9, PizzaCoin contract defines constant variables for providing token information. In the line no’s. 26 - 70, the contract implements all the functions required by ERC20 contract. Each function does nothing except forwarding the transaction request to its delegated function on PizzaCoinCodeLib2 library.


// ----------------------------------------------------------------------------
// Pizza Coin Contract
// ----------------------------------------------------------------------------
contract PizzaCoin is ERC20, Owned {
 
    // Token info
    string public constant symbol = "PZC";
    string public constant name = "Pizza Coin";
    uint8 public constant decimals = 0;
 
 

    ...
    ...
    ...
 

    
    /*
    *
    * This contract is partially compatible with ERC token standard #20 interface.
    * That is, only balanceOf() and totalSupply() would really be implemented.
    *
    */

    // ------------------------------------------------------------------------
    // Standard function of ERC token standard #20
    // ------------------------------------------------------------------------
    function totalSupply() public view returns (uint256 _totalSupply) {
        return PizzaCoinCodeLib2.totalSupply(staffContract, playerContract);
    }

    // ------------------------------------------------------------------------
    // Standard function of ERC token standard #20
    // ------------------------------------------------------------------------
    function balanceOf(address _tokenOwner) public view returns (uint256 _balance) {
        return PizzaCoinCodeLib2.balanceOf(_tokenOwner, staffContract, playerContract);
    }

    // ------------------------------------------------------------------------
    // Standard function of ERC token standard #20
    // ------------------------------------------------------------------------
    function allowance(address _tokenOwner, address _spender) public view returns (uint256) {
        // This function does nothing, just revert a transaction
        PizzaCoinCodeLib2.allowance(_tokenOwner, _spender);
    }

    // ------------------------------------------------------------------------
    // Standard function of ERC token standard #20
    // ------------------------------------------------------------------------
    function transfer(address _to, uint256 _tokens) public returns (bool) {
        // This function does nothing, just revert a transaction
        PizzaCoinCodeLib2.transfer(_to, _tokens);
    }

    // ------------------------------------------------------------------------
    // Standard function of ERC token standard #20
    // ------------------------------------------------------------------------
    function approve(address _spender, uint256 _tokens) public returns (bool) {
        // This function does nothing, just revert a transaction
        PizzaCoinCodeLib2.approve(_spender, _tokens);
    }

    // ------------------------------------------------------------------------
    // Standard function of ERC token standard #20
    // ------------------------------------------------------------------------
    function transferFrom(address _from, address _to, uint256 _tokens) public returns (bool) {
        // This function does nothing, just revert a transaction
        PizzaCoinCodeLib2.transferFrom(_from, _to, _tokens);
    }
}

Code Snippet 10. The implementation of ERC-20 token standard on PizzaCoin contract



/*
*
* This contract is partially compatible with ERC token standard #20 interface.
* That is, only balanceOf() and totalSupply() would really be implemented.
*
*/

// ------------------------------------------------------------------------
// Standard function of ERC token standard #20
// ------------------------------------------------------------------------
function totalSupply(address _staffContract, address _playerContract) 
    public view returns (uint256 _totalSupply) 
{
    assert(_staffContract != address(0));
    assert(_playerContract != address(0));

    // Get contract instances from the deployed addresses
    IStaffContract staffContractInstance = IStaffContract(_staffContract);
    IPlayerContract playerContractInstance = IPlayerContract(_playerContract);

    uint256 staffTotalSupply = staffContractInstance.getTotalSupply();
    uint256 playerTotalSupply = playerContractInstance.getTotalSupply();
    return staffTotalSupply.add(playerTotalSupply);
}

// ------------------------------------------------------------------------
// Standard function of ERC token standard #20
// ------------------------------------------------------------------------
function balanceOf( 
    address _tokenOwner, 
    address _staffContract, 
    address _playerContract
 ) 
    public view 
    returns (uint256 _balance) 
{
    assert(_staffContract != address(0));
    assert(_playerContract != address(0));

    // Get contract instances from the deployed addresses
    IStaffContract staffContractInstance = IStaffContract(_staffContract);
    IPlayerContract playerContractInstance = IPlayerContract(_playerContract);

    if (staffContractInstance.isStaff(_tokenOwner)) {
        return staffContractInstance.getTokenBalance(_tokenOwner);
    }
    else if (playerContractInstance.isPlayer(_tokenOwner)) {
        return playerContractInstance.getTokenBalance(_tokenOwner);
    }
    else {
        revert("The specified address was not being registered.");
    }
}

// ------------------------------------------------------------------------
// Standard function of ERC token standard #20
// ------------------------------------------------------------------------
function allowance(address _tokenOwner, address _spender) public pure returns (uint256) {
    // This function does nothing, just revert a transaction
    revert("We don't implement this function.");

    // These statements do nothing, just use to stop compilation warnings
    _tokenOwner == _tokenOwner;
    _spender == _spender;
}

// ------------------------------------------------------------------------
// Standard function of ERC token standard #20
// ------------------------------------------------------------------------
function transfer(address _to, uint256 _tokens) public pure returns (bool) {
    // This function does nothing, just revert a transaction
    revert("We don't implement this function.");

    // These statements do nothing, just use to stop compilation warnings
    _to == _to;
    _tokens == _tokens;
}

// ------------------------------------------------------------------------
// Standard function of ERC token standard #20
// ------------------------------------------------------------------------
function approve(address _spender, uint256 _tokens) public pure returns (bool) {
    // This function does nothing, just revert a transaction
    revert("We don't implement this function.");

    // These statements do nothing, just use to stop compilation warnings
    _spender == _spender;
    _tokens == _tokens;
}

// ------------------------------------------------------------------------
// Standard function of ERC token standard #20
// ------------------------------------------------------------------------
function transferFrom(address _from, address _to, uint256 _tokens) public pure returns (bool) {
    // This function does nothing, just revert a transaction
    revert("We don't implement this function.");

    // These statements do nothing, just use to stop compilation warnings
    _from == _from;
    _to == _to;
    _tokens == _tokens;
}

Code Snippet 11. The delegated functions on PizzaCoinCodeLib2 library which implement ERC-20 token standard


Code snippet 11 shows the implementation of the delegated functions on PizzaCoinCodeLib2 library which implement ERC-20 token standard. Actually, our voting system is partially compatible with ERC20. That is, only two ERC20 functions are really implemented including totalSupply and balanceOf functions. totalSupply function provides a total number of voting tokens supplied to all registered users in the system. For that to happen, the function queries two total supply values from PizzaCoinStaff and PizzaCoinPlayer contracts in the line no’s. 21 and 22 respectively. The function then returns its caller the sum of the two obtained total supply values.

balanceOf function provides token balance information of the specified user. The function would determine a role of the specified user. If the specified user is a staff, the function queries a token balance of that specific user from PizzaCoinStaff contract in the line no. 45. If the specified user is a player, the function will communicate with PizzaCoinPlayer contract in order to get a token balance of the specified user in the line no. 48 instead. The resulting token balance would then be returned to a caller. For other ERC20 functions, they do nothing except reverting every incoming transaction request.

Figure 5. PZC token on Cipher Browser


Since PizzaCoin voting system is compatible with ERC-20 token standard, the system can be listed on any Ethereum-supported browsers or wallets. Figure 5 shows PZC token listed on the cipher browser application.

Summary


Let’s summarize. In this article, you have learned how PizzaCoin contract was implemented to interact with its children contracts through the help of proxy libraries. You have also learned how PizzaCoin was implemented in order to be compatible with ERC-20 token standard.

Hopefully my articles would give you an idea on how Ethereum’s smart contract can be implemented on production. Please do not hesitate to ask me if you have any questions. Any suggestions would be greatly appreciated. Thank you for reading my articles.




PizzaCoin the series consists of 6 articles as follows.

Part 1: How Did We Develop Ethereum-based Voting System for Pizza Hackathon?

Part 2: Workflow Design for PizzaCoin Voting System

Part 3: Detailed Implementation of Staff and Player Contracts

Part 4: Detailed Implementation of Team Contract

Part 5: Deploying Children Contracts with Contract Factories

Part 6: Integrating PizzaCoin Contract with Dependencies


Demystifying Hyperledger Fabric (3/3): Network Traffic Handling, Service Discovery, and Operations Service

June 4, 2019
Hyperledger Fabric Hyperledger Facilitating Supplementary Services Private Blockchain Permissioned Blockchain Blockchain

Demystifying Hyperledger Fabric (2/3): Private Data Collection

May 15, 2019
Hyperledger Fabric Hyperledger Private Data Collection Private Blockchain Permissioned Blockchain Blockchain

Demystifying Hyperledger Fabric (1/3): Fabric Architecture

May 2, 2019
Hyperledger Fabric Hyperledger Fabric Architecture Private Blockchain Permissioned Blockchain Blockchain
comments powered by Disqus