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.
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);
}
}
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.
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
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.
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
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.
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