[JAVA] [Ethereum] Comment exécuter un contrat en utilisant web3j-Part 2-

Contenu de l'article

L'article suivant décrit comment exécuter un contrat à l'aide de Web3j. Dans cet article, vous allez créer une classe Java pour les opérations de contrat et exécuter le contrat via cette classe.

Comment exécuter un contrat avec web3j

Avec cette méthode, à mesure que le nombre de contrats augmente, le nombre de classes Java augmente du nombre de contrats, ce qui peut être compliqué à gérer. Par conséquent, nous décrirons comment exécuter en spécifiant l'adresse du contrat et le nom de la fonction.

environnement

Exemple de contrat

Le contrat émet des jetons ERC20 à l'aide d'Openzeppelin. Le contenu consiste à créer une classe qui hérite de ERC20.sol et à préparer uniquement le constructeur.

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"; //Nom du jeton
    string public symbol = "ORG"; //Notation d'unité du jeton
    uint public decimals = 18; //Nombre de chiffres de la virgule décimale
    
    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);
    }
}

Classe Java pour les opérations contractuelles

Vient ensuite une classe Java, qui est plutôt rudimentaire. L'idée de base peut être exécutée si vous connaissez l'adresse du contrat, le nom de la fonction, les arguments et la valeur de retour. C'est fait.

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<>();

            //Aucun argument / valeur de retour uint256
            result = exec.execFunction("totalSupply", inputParam, ResultType.INT);
            System.out.println("Total Supply : " + ((Uint)result.get(0)).getValue());

            // FROM_ADRESSE et À_Confirmation du solde de ADDRESS
            confirmBalance(OWNER_ADDRESS, TO_ADDRESS);

            //Avec argument (adresse,uint256) / valeur de retour 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<>();
            //Avec argument (adresse) / valeur de retour 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++;
        }
    }

    /**
     *Exécution de la fonction où la transaction n'a pas lieu (la valeur n'est pas mise à jour)
     * @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());
    }

    /**
     *Exécution de la fonction où la transaction se produit (la valeur est mise à jour)
     * @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 {
            //Obtenez une valeur nonce
            EthGetTransactionCount ethTransactionCount =
                web3j.ethGetTransactionCount(credentials.getAddress(), DefaultBlockParameterName.PENDING).send();
            BigInteger nonce = ethTransactionCount.getTransactionCount();

            //Génération de transaction
            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();

            //Confirmation d'erreur
            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
}

Ce que vous faites avec la méthode principale est simple. Vérifiez le montant des jetons émis, vérifiez le solde et envoyez de l'argent. Enfin, reconfirmez l'équilibre.

En d'autres termes, en se concentrant sur la création de contrat, c'est comme suit.

  1. Exécution de la fonction sans mise à jour des valeurs
  2. Exécution de la fonction avec mise à jour de la valeur

1. Exécution de la fonction sans mise à jour des valeurs

Le premier est l'exécution d'une fonction qui ne met pas à jour la valeur. Dans ERC20.sol, il y a «offre totale» et «solde de». Le code est comme suit.

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

Que fais tu

  1. Créez une instance de Function
  2. Fonction Binarize
  3. Envoyer via le réseau Ethereum
  4. Convertissez le résultat renvoyé en binaire

Dans cet exemple de code, l'argument de «execFunction» spécifie ce que l'appelant veut que le type de retour renvoie. ResultType définit quelle classe le type défini dans solidity est traité comme un type d'énumération en Java. J'essaye de renvoyer la classe qui correspond à la valeur de retour dans la méthode "getTypeReference".

Enfin, la valeur renvoyée sous forme de données binaires est décodée et renvoyée à l'appelant.

2. Exécution de la fonction avec mise à jour de la valeur

Vient ensuite un appel de fonction avec une mise à jour de valeur. C'est assez différent de faire.

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 {
            //Obtenez une valeur nonce
            EthGetTransactionCount ethTransactionCount =
                web3j.ethGetTransactionCount(credentials.getAddress(), DefaultBlockParameterName.PENDING).send();
            BigInteger nonce = ethTransactionCount.getTransactionCount();

            //Génération de transaction
            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();

            //Confirmation d'erreur
            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;
    }

Que fais tu

  1. Créez une instance de fonction
  2. Binarisation des fonctions
  3. Obtenez une valeur nonce
  4. Générer une transaction
  5. Signe
  6. Envoyer la transaction
  7. Vérifiez le résultat

Une transaction est générée car il y a une mise à jour de valeur, et la mise à jour est confirmée lorsqu'elle est prise dans le bloc. Par conséquent, la méthode d'appel est assez différente de la fonction qui ne met pas à jour la valeur.

Impressions

Personnellement, j'aime créer une classe pour chaque contrat de manière intuitive, mais si vous créez un mécanisme pour appeler un contrat et obtenir une valeur, vous pouvez créer un code assez polyvalent. C'est vrai.

Recommended Posts

[Ethereum] Comment exécuter un contrat en utilisant web3j-Part 2-
Comment trier une liste à l'aide du comparateur
Comment exécuter une méthode et simuler avec JUnit
[Rails] Comment créer un graphique à l'aide de lazy_high_charts
Comment supprimer un contrôleur, etc. à l'aide d'une commande
Comment convertir un contrat de solidité en une classe de contrat Java
Comment générer une clé primaire à l'aide de @GeneratedValue
Comment supprimer des éléments d'adaptateur personnalisés à l'aide d'un modèle personnalisé
[Java] Comment exécuter des tâches régulièrement
Comment convertir A en A et A en A en utilisant le produit logique et la somme en Java
Comment laisser un commentaire
Comment insérer une vidéo
Comment créer une méthode
Comment autoriser à l'aide de graphql-ruby
Comment générer des valeurs de tableau sans utiliser d'instruction for
Comment joindre une table sans utiliser DBFlute et SQL
Comment s'inscrire en tant que client dans Square à l'aide de Tomcat
Comment créer un conteneur Java
Comment signer Minecraft MOD
Comment créer un pilote JDBC
[Swift] Comment envoyer une notification
Comment créer un écran de démarrage
Comment créer un plug-in Jenkins
Comment faire un projet Maven
Comment créer un tableau Java
Comment exécuter l'IRB de Ruby (Ruby interactif)
Comment créer CloudStack à l'aide de Docker
Comment créer un fichier jar et un fichier war à l'aide de la commande jar
Comment créer un hinadan pour un projet Spring Boot à l'aide de SPRING INITIALIZR
[Rails 6] Comment créer un écran de saisie de formulaire dynamique à l'aide de cocoon
Comment faire un diamant révolutionnaire en utilisant Java pour déclaration wwww
Comment créer un résumé de calendrier Java
Un mémorandum sur l'utilisation d'Eclipse
Comment renvoyer une valeur du modèle au contrôleur en utilisant le protocole [Swift5]
Comment mettre en œuvre un diaporama en utilisant Slick in Rails (un par un et plusieurs par un)
[Rails] Comment télécharger des images à l'aide de Carrierwave
Comment créer une requête à l'aide de variables dans GraphQL [Utilisation de Ruby on Rails]
[Basique] Comment écrire un auto-apprentissage Dockerfile ②
Comment insérer une vidéo dans Rails
[Java] Comment calculer l'âge à l'aide de LocalDate
Comment ajouter un nouveau hachage / tableau
[Introduction à Java] Comment écrire un programme Java
Comment créer un référentiel Maven pour 2020
Comment créer un robot Discord (Java)
[Java] Comment accéder au début d'une chaîne spécifique à l'aide de la classe String
[Swift5] Comment créer un écran de démarrage
[rails] Comment créer un modèle partiel
[Swift5] Comment mettre en œuvre une animation à l'aide de "lottie-ios"
Pour implémenter la publication d'images à l'aide de rails
Comment publier une bibliothèque dans jCenter
[SpringBoot] Comment écrire un test de contrôleur