Skip to content

Best Practice of HPB Smart Contract

BlockGeek edited this page Apr 28, 2019 · 4 revisions

1 Overview

1.1 Preface

This article mainly guides how to publish the smart contract tutorial on the HPB main website through examples. Intended readers: community developers, testers, operation and maintenance personnel.

1.2 Learning preparation

1.2.1 Solidity language

Solidity is an open source, smart contract high-level language running on top of the Ethereum Virtual Machine (EVM) that supports the open source. Specific language introduction and learning introduction are illustrated in the following URLs: https://solidity.readthedocs.io/en/v0.5.2/

1.2.2 Remix

Supports smart contract development IDE, enables quick deployment of testing smart contract in browser, and supports Solidity language. Access and use address: http://remix.ethereum.org or http://remix.hpb.io

1.2.3 HPB main link access guide

Please visit HPB official website for access detail interface, which will guide you on how to interact with main chain through RPC, SDK, etc. https://www.hpb.io/client

1.2.4 Smart contract Demo address

https://github.com/loglos/web3-hpb-test.git

2 Smart contract development

2.1 Environment preparation

2.1.1 Smart contract development with online Remix compiler

Please visit the link for smart contract development http://remix.ethereum.org For the development of specific smart contract, please read Chapter 3.

2.1.2 Build a local development environment by downloading Remix open source code

Download address: https://github.com/ethereum/remix-ide

  • Installation steps

nstall npm and node.js (see https://docs.npmjs.com/getting-started/installing-node), then do:

Remix-ide has been published as an npm module:

npm install remix-ide -g
remix-ide

Or if you want to clone the github repository (wget need to be installed first) :

git clone https://github.com/ethereum/remix-ide.git
git clone https://github.com/ethereum/remix.git # only if you plan to link remix and remix-ide repositories and develop on it.
cd remix-ide
npm install
npm run setupremix  # only if you plan to link remix and remix-ide repositories and develop on it.
npm start

  • DEVELOPING:

Run npm start and open http://127.0.0.1:8080 in your browser.

Then open your text editor and start developing. The browser will automatically refresh when files are saved.

Most of the the time working with other modules (like debugger etc.) hosted in the Remix repository is not needed.

Troubleshooting building Some things to consider if you have trouble building the package:

Make sure that you have the correct version of node, npm and nvm. You can find the version that is tested on Travis CI by looking at the log in the build results. Run:

node --version
npm --version
nvm --version

In Debian based OS such as Ubuntu 14.04LTS you may need to run apt-get install build-essential. After installing build-essential run npm rebuild.

  • Unit Testing Register new unit test files in test/index.js. The tests are written using tape.

Run the unit tests via: npm test

For local headless browser tests run npm run test-browser (requires Selenium to be installed - can be done with npm run selenium-install)

Running unit tests via npm test requires at least node v7.0.0

  • Browser Testing

To run the Selenium tests via Nightwatch serve the app through a local web server:

npm run serve # starts web server at localhost:8080

Then you will need to either:

1.Have a Selenium server running locally on port 4444.

   Run: npm run test-browser

2.Or, install and run SauceConnect.

   Run: sc -u <USERNAME> -k <ACCESS_KEY> (see .travis.yml for values)
   Run: npm run browser-test-sc
  • Usage as a Chrome Extension

If you would like to use this as a Chrome extension, you must either build it first or pull from the gh-pages branch, both described above. After that, follow these steps:

1.Browse to chrome://extensions/
2.Make sure 'Developer mode' has been checked
3.Click 'Load unpacked extension...' to pop up a file-selection dialog
4.Select your remix-ide folder
  • Documentation

To see details about how to use Remix for developing and/or debugging Solidity contracts, please see our documentation page https://remix.readthedocs.io/en/latest/

2.3 Develop smart contract by Remix

2.3.1 Contract preparation

Here is a contract code for issuing ERC20 standard token. See attachment at the end of the article (1)

Open online compiler or local editor for solidity smart contract:

https://remix.ethereum.org/#optimize=true&version=soljson-v0.4.11+commit.68ef5810.js

Paste the code into the editor and select the compile version in the compiler as specified in the contract code.

2.3.2 Recompile ERC20 Token source code

Recompile the ERC20 source code, then copy the ABI data of compiled code to prepare for subsequent deployment to HPB main chain. Click for detail

2.3.3 Issue contract to HPB main chain

There are two ways to issue contract to the HPB main chain. One is through the official JAVA SDK provided by HPB and the other is through the command line after installing the node.

2.3.3.1 Environment preparation

Please read "best practice of HPB main network JAVA version to publish smart contract guide via JAVA SDK".

Article link: http://192.168.1.1 Please refer to this article Java Best Practice.

Demo project code download address: https://github.com/loglos/web3-hpb-test.git

Here we want to remind developers that it is necessary to set up new contract corresponds to ".abi" and ".bin" files that are corresponding to smart contract codes and it is also required to copy the above abi and bin codes into the corresponding file.

Here is a brief explanation of key code.

The source project needs to be converted to maven type project after downloading. Please note that the JAVA SDK package is introduced in the pom.xml file. Here we want to remind you to use the latest 2.0.3 version which fixes some bugs in the Java code in the production process.

<dependency>
			<groupId>io.hpb.web3</groupId>
			<artifactId>web3-hpb</artifactId>
			<version>2.0.3</version>
</dependency>


2.3.3.2 Demo source description

The demo program is under this package

package io.hpb.web3.test;


The demo java class demonstrated in the package is described here.

  • UFOToken.sol: ERC20 smart contract source code for this demo
  • UFOToken.abi: abi source code compiled by smart contract
  • UFOToken.bin: Bin file after compiling the smart contract
  • UFOTokenRun.java: Task executer of smart contract object class generated by Java
  • UFOToken.java: Java source code for smart contract mapping automatically generated by the system.

2.3.3.2 UFOTokenRun execution instructions

Developers can directly execute the main method in UFOTokenRun.java to debug.

Please fill in the path of the relevant address based on the actual calling address and HPB account information in the development environment.

package io.hpb.web3.test;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;

import io.hpb.web3.codegen.SolidityFunctionWrapperGenerator;
import io.hpb.web3.crypto.Credentials;
import io.hpb.web3.crypto.WalletUtils;
import io.hpb.web3.protocol.Web3;
import io.hpb.web3.protocol.Web3Service;
import io.hpb.web3.protocol.admin.Admin;
import io.hpb.web3.protocol.core.DefaultBlockParameterName;
import io.hpb.web3.protocol.core.methods.response.TransactionReceipt;
import io.hpb.web3.protocol.http.HttpService;
import io.hpb.web3.tx.ChainId;
import io.hpb.web3.tx.RawTransactionManager;
import io.hpb.web3.utils.Convert;
import okhttp3.OkHttpClient;
import io.hpb.web3.abi.datatypes.Address;
import io.hpb.web3.abi.datatypes.generated.Uint256;

public class UFOTokenRun {

	
	//Publish smart contract account, the full path of keystore, please modify according to the actual address
	private static String keyStoreAbsolutePath = "/path/UTC--2018-5cb988b9ce48fd3b5a328b582dd64f5c10d0e114"; 
    //Publish smart contract account and password, please use your own account and password.
	private static String fromPassword = "demo111";
	//Publish smart contract address: This is the UFOToken smart contract address that has been published to the main network. The user can make an inquiry, but cannot transfer. The transfer can be made only with HPB balance.
	private static String address = "0xfbbe0ba33812b531aced666d0bb2450216c11d11";
    //Open HPB node URL address, you can also set up your own node; this node is open in HPB official network
	private static String blockChainUrl = "http://pub.node.hpb.io/";
    
	//System default parameter settings
	private static BigInteger GAS_PRICE = new BigInteger("18000000000");

	private static BigInteger GAS_LIMIT = new BigInteger("100000000");
    
    
	public static void main(String[] args) {
 
	  //Specify the package path for generating smart contract java mapping class
      String packageName = "io.hpb.web3.test";
	  //Specify the local storage address of source code in smart contract java mapping class
      String outDirPath = "//erc20//UFO//java";
      
	  //Specify the local address of smart contract source code. These two files are also placed under the package of this class, and reader can handle it by themselves.
      String binFilePath = "//erc20//UFO//bin//UFOToken.bin";
      String abiFilePath = "//erc20//UFO//bin//UFOToken.abi";


	  //1 Generate the mapping class of the smart contract sol source code  through io.hpb.web3; then put the mapping class into the corresponding package
      GenContractJavaCode(packageName, binFilePath, abiFilePath, outDirPath) ;


	  //2 Publish smart contract and get the address
      String address = depolyUFOTokenTest();
	
      //3 Get the balance of smart contract
      getUFOTokenContractBalance();
		
	
	  //Check erc20 balance of specified address
      String  queryAddress = "0xd8ACcED8A7A92b334007f9C6127A848fE51D3C3b";
	  //4 Verify smart contract and print relevant information
      checkUFOTokenContract(queryAddress);
		
	
	  //5 Transfer
      String  toAddress = "0x6cb988b9ce48Fd3b5a328B582dd64F5C10d0E114";
      transferUFOTokenContract(toAddress,"13333333000000000000000000");
 
		//Check balance
		//checkUFOTokenContract(contractAddress,toAddress);
		
	}
	

	/**
	 * Java source code generated by source code .sol file after smart contract and .bin file after compilation.
	 * String packageName: packagename of java source code
	 * String binFileName: smart contract bin file storage address
	 * String abiFileName: smart contract abi file storage address 
	 * String outDirPath : java source output address 
	 * 
	 * **/
	public static void GenContractJavaCode(String packageName,String binFilePath,String abiFilePath,String outDirPath) {
		   try {
		        String SOLIDITY_TYPES_ARG = "--solidityTypes";
		        
		        SolidityFunctionWrapperGenerator.main(Arrays.asList(SOLIDITY_TYPES_ARG,
		        		binFilePath,abiFilePath,"-p",packageName, "-o", outDirPath).toArray(new String[0]));
		    } catch (Exception e) {
		        e.printStackTrace();
		    }

	}

	


	/**
	 * Obtain the contract mapping java class by compiling smart contract source code
	 * **/
	public static String depolyUFOTokenTest(){
	
	    Credentials credentials = null;
	    Admin admin = null;

        String contractAddress =  "";
	    try{
	        Web3Service web3Service = new HttpService(blockChainUrl, new OkHttpClient.Builder().build(), true);
	        admin = Admin.build(web3Service);
	        credentials = WalletUtils.loadCredentials(fromPassword, keyStoreAbsolutePath);
	        RawTransactionManager transactionManager=new RawTransactionManager(admin, credentials, ChainId.MAINNET);

	        //Publish TOKEN
	        UFOToken contract = UFOToken.deploy(admin, transactionManager, GAS_PRICE, GAS_LIMIT).send();
	        System.out.println("smartaddress:" + contract.getContractAddress());
	        contractAddress = contract.getContractAddress();

	  	        System.out.println("Smart contract:" + contract.getContractAddress());
	        contractAddress = contract.getContractAddress();

	    }catch (Exception e){
	        e.printStackTrace();
	    }
	    return contractAddress;
	}
	
	
	/**
	 * Check balance
	 * **/
	public static BigDecimal getUFOTokenContractBalance(){
		
	    Credentials credentials = null;
	    Admin admin = null;
	    BigDecimal balanceWeiAmt = null;

	    try{
	        
	        Web3Service web3Service = new HttpService(blockChainUrl, new OkHttpClient.Builder().build(), true);
	        admin = Admin.build(web3Service);
	  
	        BigInteger balance = admin.hpbGetBalance(address, DefaultBlockParameterName.LATEST).send().getBalance();
	        balanceWeiAmt = Convert.fromWei(balance.toString(), Convert.Unit.HPB);
            System.out.println(address + "Account balance:" + balanceWeiAmt);
	    }catch (Exception e){
	        e.printStackTrace();
	    }
	    
	    return balanceWeiAmt;
	}
	
	
	
	/**
	 * Verify smart contract and print address ERC20 balance
	 * **/
	public static void checkUFOTokenContract(String queryAddress){
		 
	    Credentials credentials = null;
	    Admin admin = null;
	    
	    try{
	        
	        Web3Service web3Service = new HttpService(blockChainUrl, new OkHttpClient.Builder().build(), true);
	        admin = Admin.build(web3Service);
	        credentials = WalletUtils.loadCredentials(fromPassword, keyStoreAbsolutePath);
	        RawTransactionManager transactionManager=new RawTransactionManager(admin, credentials, ChainId.MAINNET);
	        
	        //Check whether the contract is effective

	        UFOToken contract = UFOToken.load(address, admin, transactionManager, GAS_PRICE, GAS_LIMIT);
	        System.out.println("please check if contract valid:" +contract.isValid() );
			System.out.println("Verify the contract is valid:" +contract.isValid() );

	        if(contract.isValid()) {
		        BigInteger totalSupply = contract.totalSupply().send().getValue().divide(new BigInteger("1000000000000000000"));	        
		        System.out.println("UFOtoken Total supply:"+totalSupply);
	        	    System.out.println(address+" UFOToken Balance:"+contract.balanceOf(new Address(address)).sendAsync().get().getValue().divide(new BigInteger("1000000000000000000")));	  			        
		        System.out.println(queryAddress+" UFOToken Balance:"+contract.balanceOf(new Address(queryAddress)).sendAsync().get().getValue().divide(new BigInteger("1000000000000000000")));	  
	        }
	        


	    }catch (Exception e){
	        e.printStackTrace();
	    }
	}
	
	public String toDecimal(int decimal, BigInteger integer) {
        StringBuffer sbf = new StringBuffer("1");
        for (int i = 0; i < decimal; i++) {
            sbf.append("0");
        }
        String balance = new BigDecimal(integer).divide(new BigDecimal(sbf.toString()), 18, BigDecimal.ROUND_DOWN).toPlainString();
        return balance;
    }



	/**
	 * Transfer
	 * **/
	public static void transferUFOTokenContract(String toAddress,String toValue){
		//keystore full path
	    Credentials credentials = null;
	    Admin admin = null;
	    try{
	        
	        Web3Service web3Service = new HttpService(blockChainUrl, new OkHttpClient.Builder().build(), true);
	        admin = Admin.build(web3Service);
	        credentials = WalletUtils.loadCredentials(fromPassword, keyStoreAbsolutePath);
	        RawTransactionManager transactionManager=new RawTransactionManager(admin, credentials, ChainId.MAINNET);

	        //Transfer transaction
	        UFOToken contract = UFOToken.load(address, admin, transactionManager, GAS_PRICE, GAS_LIMIT);
	        TransactionReceipt receipt =  contract.transfer(new Address(toAddress), new Uint256(new BigInteger(toValue))).send();
	        System.out.println("Transaction Hash::::::"+receipt.getTransactionHash());
	        
	    }catch (Exception e){
	        e.printStackTrace();
	    }
	}
 

}




Attachment 1 ERC20 Smart Contract Code

//Specify the use of solidity development language version
pragma solidity ^0.4.19;
/**
 * @title SafeMath
 * @dev Math operations with safety checks that throw on error
 */
library SafeMath {
  //Basic arithmetic method library
  //Multiple
  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
    if (a == 0) {
      return 0;
    }
    uint256 c = a * b;
    assert(c / a == b);
    return c;
  }
  
  //Divide
  function div(uint256 a, uint256 b) internal pure returns (uint256) {
    // assert(b > 0); // Solidity automatically throws when dividing by 0
    uint256 c = a / b;
    // assert(a == b * c + a % b); // There is no case in which this doesn't hold
    return c;
  }
  //Deduct
  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
    assert(b <= a);
    return a - b;
  }
  //Add
  function add(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c = a + b;
    assert(c >= a);
    return c;
  }
}
/**
 * @title ERC20Basic
 * @dev Simpler version of ERC20 interface
 * @dev see https://github.com/ethereum/EIPs/issues/179
 */
contract ERC20Basic {
  //Total supply, namely the total amount of money
  uint256 public totalSupply;
  //Check the balance of designated address
  function balanceOf(address who) public view returns (uint256);
  //Recommended transfer method can be safely written
  function transfer(address to, uint256 value) public returns (bool);
  //Logging
  //address indexed fromaddress indexed to, uint256 value:
  event Transfer(address indexed from, address indexed to, uint256 value);
}
/**
 * @title ERC20 interface
 * @dev see https://github.com/ethereum/EIPs/issues/20
 */
contract ERC20 is ERC20Basic {
  //, _owner  _spender check the number of token can be withdrawn from address _owner  
  function allowance(address owner, address spender) public view returns (uint256);
  
  // When A-B from address _ transfer token to address _ , it must trigger Transfer event
  function transferFrom(address from, address to, uint256 value) public returns (bool);
  // When _spender  _value successfully call approve, it triggers 
  function approve(address spender, uint256 value) public returns (bool);
  
  //triggers event record log
  event Approval(address indexed owner, address indexed spender, uint256 value);
}
/**
 * @title Basic token
 * @dev Basic version of StandardToken, with no allowances.
 */
contract BasicToken is ERC20Basic {
  //SafeMathLibrary,
  using SafeMath for uint256;
  //
  mapping(address => uint256) balances;
  //_valuetoken_to
  /**
  * @dev transfer token for a specified address
  * @param _to The address to transfer to.
  * @param _value The amount to be transferred.
  */
  function transfer(address _to, uint256 _value) public returns (bool) {
    //require
    //address(0) the address should be valid
    require(_to != address(0));
    
    //balances:
    //msg.sender
    //Sender balance is not 0
    require(_value <= balances[msg.sender]);
    // SafeMath.sub will throw if there is not enough balance.
    //Sender balance is deducted
    balances[msg.sender] = balances[msg.sender].sub(_value);
    //Sender balance is added
    balances[_to] = balances[_to].add(_value);
    //Transaction done
    Transfer(msg.sender, _to, _value);
    return true;
  }
  //// balanceOffunctionbalanceOf(address _owner)constantreturns(uint256 balance)
  /**
  * @dev Gets the balance of the specified address.
  * @param _owner The address to query the the balance of.
  * @return An uint256 representing the amount owned by the passed address.
  */
  function balanceOf(address _owner) public view returns (uint256 balance) {
    return balances[_owner];
  }
}
/**
 * @title Standard ERC20 token
 *
 * @dev Implementation of the basic standard token.
 * @dev https://github.com/ethereum/EIPs/issues/20
 */
contract StandardToken is ERC20, BasicToken {
  mapping (address => mapping (address => uint256)) internal allowed;
  /**
   * @dev Transfer tokens from one address to another
   * @param _from address The address which you want to send tokens from
   * @param _to address The address which you want to transfer to
   * @param _value uint256 the amount of tokens to be transferred
   */
  function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
    require(_to != address(0));
    require(_value <= balances[_from]);
    require(_value <= allowed[_from][msg.sender]);
    balances[_from] = balances[_from].sub(_value);
    balances[_to] = balances[_to].add(_value);
    allowed[_from][msg.sender] = allowed[_from][msg.sender].sub(_value);
    Transfer(_from, _to, _value);
    return true;
  }
  /**
   * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.
   *
   * Beware that changing an allowance with this method brings the risk that someone may use both the old
   * and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this
   * race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards:
   * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
   * @param _spender The address which will spend the funds.
   * @param _value The amount of tokens to be spent.
   */
  function approve(address _spender, uint256 _value) public returns (bool) {
    allowed[msg.sender][_spender] = _value;
    Approval(msg.sender, _spender, _value);
    return true;
  }
  /**
   * @dev Function to check the amount of tokens that an owner allowed to a spender.
   * @param _owner address The address which owns the funds.
   * @param _spender address The address which will spend the funds.
   * @return A uint256 specifying the amount of tokens still available for the spender.
   */
  function allowance(address _owner, address _spender) public view returns (uint256) {
    return allowed[_owner][_spender];
  }
}
/// @title UFO Protocol Token.
/// For more information about this token, please visit http://www.banyanbbt.org
contract UFOToken is StandardToken {
    string public name;
    string public symbol;
    uint public decimals;
    /**
     * CONSTRUCTOR 
     * 
     * @dev Initialize the UFO Token
     */
    function UFOToken() public {      
        totalSupply = 10 * 10 ** 26;
        balances[msg.sender] = totalSupply;
        name = "UFOToken";
        symbol = "UFO";
        decimals = 18;
    }
}


Clone this wiki locally