[JAVA] [Ethereum] How to execute a contract using web3j-Part 2-

Article content

The following article describes how to execute a contract using Web3j. In this article, you will create a Java class for contract operations and execute the contract via that class.

How to execute a contract using web3j

With this method, as the number of contracts increases, the number of Java classes increases by the number of contracts, which can be complicated to manage. Therefore, we will describe how to execute by specifying the contract address and function name.

environment

Sample contract

The contract is to issue an ERC20 token using Openzeppelin. The content is to create a class that inherits ERC20.sol and prepare only the constructor.

MyToken.sol


pragma solidity ^0.4.24;

import "node_modules/openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";

contract MyToken is ERC20 {

    string public name = "OrgToken"; //Token name
    string public symbol = "ORG"; //Token unit notation
    uint public decimals = 18; //Number of decimal places
    
    address public account = msg.sender;
    uint256 public _totalS = 10000000000000;

    constructor () public {
        super._mint(account, _totalS);
    }

    function balanceOf(address target) public view returns (uint256) {
        return super.balanceOf(target);
    }
}

Java class for contract operations

Next is a Java class, which is rather crude. The basic idea can be executed if you know the contract address, function name, arguments, and return value. It is made.

MyTokenExec.Java


import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutionException;

import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.FunctionReturnDecoder;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Bool;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.Uint;
import org.web3j.abi.datatypes.Utf8String;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.RawTransaction;
import org.web3j.crypto.TransactionEncoder;
import org.web3j.protocol.admin.Admin;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.request.Transaction;
import org.web3j.protocol.core.methods.response.EthCall;
import org.web3j.protocol.core.methods.response.EthGetTransactionCount;
import org.web3j.protocol.core.methods.response.EthSendTransaction;
import org.web3j.protocol.http.HttpService;
import org.web3j.utils.Numeric;

public class MyContractExec {

    public static final Admin web3j = Admin.build(new HttpService("http://127.0.0.1:8545"));

    private static final String CONTRACT_ADDRESS = "0x715d4c293482e6f72e1cbb3b22a10fbdae3bd7b0";

    public static final String OWNER_ADDRESS = "0x945cd603a6754cb13c3d61d8fe240990f86f9f8a";
    public static final String TO_ADDRESS = "0x66b4e7be902300f9a15d900822bbd8803be87391";

    public static void main(String args[]) {

        MyContractExec exec = new MyContractExec();

        List<?> result = null;
        try {
            List<Type> inputParam = new ArrayList<>();

            //No arguments / return value uint256
            result = exec.execFunction("totalSupply", inputParam, ResultType.INT);
            System.out.println("Total Supply : " + ((Uint)result.get(0)).getValue());

            // FROM_ADRESS and TO_Balance confirmation of ADDRESS
            confirmBalance(OWNER_ADDRESS, TO_ADDRESS);

            //With argument (address,uint256) / return value bool
            inputParam = new ArrayList<>();
            inputParam.add(new Address(TO_ADDRESS));
            inputParam.add(new Uint(BigInteger.valueOf(123456)));
            result = sendSignedTransaction(credentials, "transfer", inputParam, ResultType.BOOL);
            System.out.println( ((Bool)result.get(0)).getValue() );

            confirmBalance(OWNER_ADDRESS, TO_ADDRESS);

        }catch(IOException e) {
            e.printStackTrace();
        }
    }

    public static void confirmBalance(String ... args) throws IOException{

        MyContractExec exec = new MyContractExec();

        List<?> result = null;
        int i = 0;
        for(String address :args) {
            List<Type> inputParam = new ArrayList<>();
            //With argument (address) / return value uint256
            inputParam.add(new Address(address));
            result = exec.execFunction("balanceOf", inputParam, ResultType.INT);
            System.out.println("Balance of ADDRESS[" + i + "] : " +  ((Uint)result.get(0)).getValue() );
            i++;
        }
    }

    /**
     *Execution of function that does not generate Transaction (value is not updated)
     * @param functionName
     * @return
     * @throws IOException
     */
    public List<?> execFunction(String functionName, List<Type> args, ResultType type) throws IOException{

        Function function = new Function(
                functionName, args, Arrays.<TypeReference<?>>asList( getTypeReference(type) ));

        String encodedFunction = FunctionEncoder.encode(function);
        EthCall ethCall = web3j.ethCall(
                Transaction.createEthCallTransaction(
                        OWNER_ADDRESS, CONTRACT_ADDRESS, encodedFunction),
                DefaultBlockParameterName.LATEST)
                .send();

        String value = ethCall.getValue();
        return FunctionReturnDecoder.decode(value, function.getOutputParameters());
    }

    /**
     *Execution of function in which Transaction occurs (value is updated)
     * @param credentials
     * @param functionName
     * @param args
     * @param type
     * @return
     */
    public static List<?> sendSignedTransaction(Credentials credentials, String functionName, List<Type> args, ResultType type){

        Function function = new Function(
                functionName, args, Arrays.<TypeReference<?>>asList( getTypeReference(type) ));

        String encodedFunction = FunctionEncoder.encode(function);

        try {
            //Get nonce value
            EthGetTransactionCount ethTransactionCount =
                web3j.ethGetTransactionCount(credentials.getAddress(), DefaultBlockParameterName.PENDING).send();
            BigInteger nonce = ethTransactionCount.getTransactionCount();

            //Transaction generation
            RawTransaction rawTransaction = RawTransaction.createTransaction(
                    nonce,
                    BigInteger.valueOf(1000),
                    BigInteger.valueOf(4700000),
                    CONTRACT_ADDRESS,
                    encodedFunction);

            //signature
            byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
            String hexValue = Numeric.toHexString(signedMessage);

            //send
            EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).sendAsync().get();

            //Error confirmation
            if(ethSendTransaction.getError() != null){
               System.out.println(ethSendTransaction.getError().getMessage());
            }else {
                String value = ethSendTransaction.getResult();
                return FunctionReturnDecoder.decode(value, function.getOutputParameters());
            }
        }catch(IOException | ExecutionException | InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static TypeReference<?> getTypeReference(ResultType type){

        TypeReference<?> typeReference = null;
        switch(type) {
        case ADDRESS:
            typeReference = new TypeReference<Address>() {};
            break;
        case BOOL:
            typeReference = new TypeReference<Bool>() {};
            break;
        case STRING:
            typeReference = new TypeReference<Utf8String>() {};
            break;
        case INT:
            typeReference = new TypeReference<Uint>() {};
            break;
        default:
            break;
        }
        return typeReference;
    }
}

ResultType.java


public enum ResultType {
	ADDRESS,
	BOOL,
	STRING,
	INT
}

What you are doing with the main method is simple. Check the amount of tokens issued, check the balance, and remit. Finally, reconfirm the balance.

In other words, focusing on the creation of contract, it is as follows.

  1. Function execution without updating values
  2. Function execution with value update

1. Function execution without updating values

The first is the execution of a function that does not update the value. In ERC20.sol, there are "total Supply" and "balance Of". The code is as follows.

MyTokenExec.java


    public List<?> execFunction(String functionName, List<Type> args, ResultType type) throws IOException{

        Function function = new Function(
                functionName, args, Arrays.<TypeReference<?>>asList( getTypeReference(type) ));

        String encodedFunction = FunctionEncoder.encode(function);
        EthCall ethCall = web3j.ethCall(
                Transaction.createEthCallTransaction(
                        OWNER_ADDRESS, CONTRACT_ADDRESS, encodedFunction),
                DefaultBlockParameterName.LATEST)
                .send();

        String value = ethCall.getValue();
        return FunctionReturnDecoder.decode(value, function.getOutputParameters());
    }

What you are doing

  1. Create an instance of Function
  2. Binary Function
  3. Send on Ethereum network
  4. Convert the result returned in binary

In this sample code, the argument of "execFunction" specifies what the caller wants to return as the return type. ResultType defines which class Java treats the type set in solidity as an enumeration type. I am trying to return the class that matches the return value in the "getTypeReference" method.

Finally, the value returned as binary data is decoded and returned to the caller.

2. Function execution with value update

Next is a function call with a value update. This is quite different in making.

MyTokenExec.java


    public static List<?> sendSignedTransaction(Credentials credentials, String functionName, List<Type> args, ResultType type){

        Function function = new Function(
                functionName, args, Arrays.<TypeReference<?>>asList( getTypeReference(type) ));

        String encodedFunction = FunctionEncoder.encode(function);

        try {
            //Get nonce value
            EthGetTransactionCount ethTransactionCount =
                web3j.ethGetTransactionCount(credentials.getAddress(), DefaultBlockParameterName.PENDING).send();
            BigInteger nonce = ethTransactionCount.getTransactionCount();

            //Transaction generation
            RawTransaction rawTransaction = RawTransaction.createTransaction(
                    nonce,
                    BigInteger.valueOf(1000),    //GAS
                    BigInteger.valueOf(4700000), //GASLimit
                    CONTRACT_ADDRESS,
                    encodedFunction);

            //signature
            byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
            String hexValue = Numeric.toHexString(signedMessage);

            //send
            EthSendTransaction ethSendTransaction = web3j.ethSendRawTransaction(hexValue).sendAsync().get();

            //Error confirmation
            if(ethSendTransaction.getError() != null){
               System.out.println(ethSendTransaction.getError().getMessage());
            }else {
                String value = ethSendTransaction.getResult();
                return FunctionReturnDecoder.decode(value, function.getOutputParameters());
            }
        }catch(IOException | ExecutionException | InterruptedException e) {
            e.printStackTrace();
        }
        return null;
    }

What you are doing

  1. Create a Function instance
  2. Function binarization
  3. Get nonce value
  4. Transaction generation
  5. Signature
  6. Send transaction
  7. Check the result

A transaction is generated because there is a value update, and the update is confirmed when it is taken into the block. Therefore, the calling method is quite different from the function that does not update the value.

Impressions

I personally like to create a class for each contract intuitively, but if you create a mechanism to call the contract and get the value, you can create the code quite universally. That's right.

Recommended Posts

[Ethereum] How to execute a contract using web3j-Part 2-
How to sort a List using Comparator
How to execute and mock methods using JUnit
[Rails] How to create a graph using lazy_high_charts
How to delete a controller etc. using a command
How to convert a solidity contract to a Java contract class
How to generate a primary key using @GeneratedValue
How to delete custom Adapter elements using a custom model
[Java] How to execute tasks on a regular basis
How to convert A to a and a to A using AND and OR in Java
[Rails] How to install a decorator using gem draper
How to leave a comment
How to insert a video
How to create a method
How to authorize using graphql-ruby
How to output array values without using a for statement
How to join a table without using DBFlute and sql
How to register as a customer with Square using Tomcat
How to add columns to a table
How to make a Java container
How to sign a Minecraft MOD
How to make a JDBC driver
[Java] How to create a folder
How to write a ternary operator
[Swift] How to send a notification
How to make a splash screen
How to make a Jenkins plugin
How to make a Maven project
How to make a Java array
How to execute Ruby irb (interactive ruby)
How to build CloudStack using Docker
How to create a jar file or war file using the jar command
How to make a hinadan for a Spring Boot project using SPRING INITIALIZR
How to run a mock server on Swagger-ui using stoplight/prism (using AWS/EC2/Docker)
[Rails 6] How to create a dynamic form input screen using cocoon
How to make a groundbreaking diamond using Java for statement wwww
[Xcode] How to add a README.md file
How to make a Java calendar Summary
A memorandum on how to use Eclipse
How to redo a deployment on Heroku
How to return a value from Model to Controller using the [Swift5] protocol
How to implement a slideshow using slick in Rails (one by one & multiple by one)
[Rails] How to upload images using Carrierwave
How to create a query using variables in GraphQL [Using Ruby on Rails]
How to build a Ruby on Rails environment using Docker (for Docker beginners)
[Basic] How to write a Dockerfile Self-learning ②
How to insert a video in Rails
[Java] How to calculate age using LocalDate
How to add a new hash / array
[Introduction to Java] How to write a Java program
How to create a Maven repository for 2020
How to make a Discord bot (Java)
[Java] How to get to the front of a specific string using the String class
How to print a Java Word document
[Swift5] How to create a splash screen
[rails] How to create a partial template
[Swift5] How to implement animation using "lottie-ios"
How to implement image posting using rails
How to make asynchronous pagenations using Kaminari
How to publish a library in jCenter
[SpringBoot] How to write a controller test