1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

VSCodeでSpring+Hardhat

Last updated at Posted at 2023-07-11

はじめに

サーバーから、定型のスマートコントラクトをデプロイするために
JavaのサーバーはSpring、スマートコントラクト開発はHardhatという環境を作成した備忘録です。

VSCodeにはあらかじめ、JavaやSpringのプラグインがインストールされていることを想定しています。

また、Nodeがインストールされていることを想定しています。

Spring

VSCodeでSpringプロジェクトを作成します。

コマンドパレット...から、Spring Initializr: Create a Maven Project...を選択してプロジェクトを作成します。

Gradle+Hardhatが上手く動きませんでした。(原因不明)

設定値は環境に合わせて変えてください。
以下は、サンプルで利用した設定値となります。

項目 設定値
Specify Spring Boot version. 2.7.13
Specify project language. java
Input Group id com.example
Input Artifact Id springhardhat
Sprcify packaging type. Jar
Sprcify java version. 11
Choose dependencies LombokとSpring Web

Choose dependenciesは、ThymeleafValidationSpring Securityなどを設定するのが一般的だと思います。

最後に保存先のフォルダを選択するダイアログが出るので適当な場所を選択します。

フォルダ構成は以下のようになります。
targetはないかもしれませんが、しばらく経つと出現します。

springhardhat
├─.mvn
├─.vscode
├─src
├─target
├─.gitignore
├─HELP.md
├─mvnw
├─mvnw.cmd
└─pom.xml

Hardhat

インストール

初期化とhardhatと必要なライブライをインストールします。

npm init -y
npm install --save-dev hardhat
npm install --save-dev @openzeppelin/contracts
npm install --save-dev @nomicfoundation/hardhat-toolbox

※:環境により、まあまあ時間がかかります。

プロジェクトの作成

以下のコマンドでプロジェクトの作成がはじまります。

npx hardhat

Create a JavaScript projectを選びJavaScriptのプロジェクトを選択します。

888    888                      888 888               888
888    888                      888 888               888
888    888                      888 888               888
8888888888  8888b.  888d888 .d88888 88888b.   8888b.  888888
888    888     "88b 888P"  d88" 888 888 "88b     "88b 888
888    888 .d888888 888    888  888 888  888 .d888888 888
888    888 888  888 888    Y88b 888 888  888 888  888 Y88b.
888    888 "Y888888 888     "Y88888 888  888 "Y888888  "Y888

Welcome to Hardhat v2.16.1

? What do you want to do? ... 
> Create a JavaScript project
  Create a TypeScript project
  Create an empty hardhat.config.js
  Quit

Hardhat project root:は、Springプロジェクトと同じにします。
Do you want to add a .gitignore?も追記してほしいので、Yを選択します。

888    888                      888 888               888
888    888                      888 888               888
888    888                      888 888               888
8888888888  8888b.  888d888 .d88888 88888b.   8888b.  888888
888    888     "88b 888P"  d88" 888 888 "88b     "88b 888
888    888 .d888888 888    888  888 888  888 .d888888 888
888    888 888  888 888    Y88b 888 888  888 888  888 Y88b.
888    888 "Y888888 888     "Y88888 888  888 "Y888888  "Y888

Welcome to Hardhat v2.16.1

√ What do you want to do? · Create a JavaScript project
√ Hardhat project root: · C:\\xxxxxxxxxxxxxxxxxxx\\springhardhat
√ Do you want to add a .gitignore? (Y/n) · y

You need to install these dependencies to run the sample project:
  npm install --save-dev "hardhat@^2.16.1" "@nomicfoundation/hardhat-toolbox@^3.0.0"

Project created

See the README.md file for some example tasks you can run

Give Hardhat a star on Github if you're enjoying it!

     https://github.com/NomicFoundation/hardhat

フォルダ構成は以下となります。
フォルダとして、contracts node_modules scripts testが増追加されています。。
ファイルは、hardhat.config.js package-lock.json package.json README.md が追加されています。
また、.gitignore にはHardhat関連のフォルダが追記されています。

springhardhat
├─.mvn
├─.vscode
├─contracts
├─node_modules
├─scripts
├─src
├─target
├─test
├─.gitignore
├─hardhat.config.js
├─HELP.md
├─mvnw
├─mvnw.cmd
├─package-lock.json
├─package.json
├─pom.xml
└─README.md

環境ファイル設定

テスト用にHardhatの環境ファイルhardhat.config.jsを設定します。
内容はアカウントを4つ明示的に追加しています。

hardhat.config.js
require("@nomicfoundation/hardhat-toolbox");

/** @type import('hardhat/config').HardhatUserConfig */
module.exports = {
  solidity: "0.8.18",
  networks: {
    hardhat: {
      accounts: [
        {
          privateKey:
            "0x0000000000000000000000000000000000000000000000000000000000000001",
          balance: "10000000000000000000000",
        },
        {
          privateKey:
            "0x0000000000000000000000000000000000000000000000000000000000000002",
          balance: "10000000000000000000000",
        },
        {
          privateKey:
            "0x0000000000000000000000000000000000000000000000000000000000000003",
          balance: "10000000000000000000000",
        },
        {
          privateKey:
            "0x0000000000000000000000000000000000000000000000000000000000000004",
          balance: "10000000000000000000000",
        },
      ],
    },
  },

};

コントラクト作成

簡単なERC20を作成します。
contractsフォルダ配下にToken.solというコントラクトを作成します。

Token.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.7 <0.9.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract Token is ERC20, Ownable {
    constructor(string memory name, string memory symbol) ERC20(name, symbol) {}

    function mint(address account, uint256 amount) public onlyOwner {
        _mint(account, amount);
    }

    function burn(address account, uint256 amount) public onlyOwner {
        _burn(account, amount);
    }
}

テストコードも作成します。
testフォルダ配下にToken.jsを作成します。

Token.js
const {
    loadFixture,
} = require("@nomicfoundation/hardhat-network-helpers");
const { expect } = require("chai");

describe("Token", function () {
    async function deployContract() {
        const [owner, user1] = await ethers.getSigners();
        const Contract = await ethers.getContractFactory("Token");
        const name = "test token";
        const symbol = "tts";
        const contract = await Contract.deploy(name, symbol);
        return { contract, name, symbol, owner, user1, };
    }
    describe("Deployment", function () {
        it("Check name and symbol", async function () {
            const { contract, name, symbol, owner, user1 } = await loadFixture(deployContract);
            expect(await contract.name()).to.equal(name);
            expect(await contract.symbol()).to.equal(symbol);
        });
        it("mint and burn", async function () {
            const { contract, name, symbol, owner, user1 } = await loadFixture(deployContract);
            let mintAmount = 10000;
            let burnAmount = 5000;
            expect(await contract.balanceOf(user1.address)).to.equal(0);
            await contract.mint(user1.address, mintAmount);
            expect(await contract.balanceOf(user1.address)).to.equal(mintAmount);
            await contract.burn(user1.address, burnAmount);
            expect(await contract.balanceOf(user1.address)).to.equal(mintAmount - burnAmount);
        });
    });
});

テスト実行は以下のコマンドとなります。

npx hardhat test test\Token.js

問題がなければ以下のような結果になります。

 npx hardhat test test\Token.js


  Token
    Deployment
      ✔ Check name and symbol (1017ms)
      ✔ mint and burn


  2 passing (1s)

Solidity→Java

Solidity(Token.sol)をJavaのコードに変換します。

準備

Solidityのコンパイラー

Solidityのコンパイラーをダウンロードします。
https://github.com/ethereum/solidity/releases

ダウンロードしたら、適当なフォルダに配置します。
ここでは、C:\tools\に展開しています。

web3j-cli(Java変換)

web3j-cliをダウンロードします。
https://github.com/web3j/web3j-cli/releases

ここではJava11を利用している為、1.4.2をダウンロードしています。
現在、1.5.0がリリースされており、Java17の場合はそちらを利用した方が良さそうです。(未検証)

ダウンロードしたら、適当なフォルダに配置します。
ここでは、C:\tools\に展開しています。

出力フォルダ(コンパイル時)

出力フォルダをoutとしています。(特にフォルダ生成する必要はありません。)
一時フォルダなので、.gitignoreに追記します。

pom.xml追記

pom.xml<dependencies>タグの中に以下の<dependency>(2つ)記述します。

pom.xml
		<!-- https://mvnrepository.com/artifact/org.web3j/core -->
		<dependency>
			<groupId>org.web3j</groupId>
			<artifactId>core</artifactId>
			<version>4.9.8</version>
		</dependency>

		<!-- https://mvnrepository.com/artifact/org.web3j/contracts -->
		<dependency>
			<groupId>org.web3j</groupId>
			<artifactId>contracts</artifactId>
			<version>4.9.8</version>
		</dependency>

ここでは、web3j-cliが1.4.2を選択している為、4.9.x系の最新を設定しています。
web3j-cliが1.5.0の場合は4.10.x系を指定した方がよさそうです。(未検証)

batchファイル作成

SolidityをJavaに変換するbatchファイルを作成します。
基本的には、Solidityをコンパイルしてコンパイル結果からJavaコードへ変換するという流れになります。

soltojava.bat
@echo off
@REM https://github.com/ethereum/solidity/releases
set SOLC=C:\tools\solc\bin\solc-windows.exe
@REM https://github.com/web3j/web3j-cli/releases
set WEB3J=C:\tools\web3j-1.4.2\bin\web3j.bat
set CONTRACT_NAME=Token
set JAVA_PACKAGE=com.example.springhardhat.contracts

call %SOLC% --version
call %WEB3J% -version

pushd %~dp0

call rmdir /s /q .\out

call %SOLC% ./contracts/%CONTRACT_NAME%.sol --bin --abi --optimize -o ./out --base-path ./contracts --include-path ./node_modules/

call %WEB3J% generate solidity -b ./out/%CONTRACT_NAME%.bin -a ./out/%CONTRACT_NAME%.abi -o ./src/main/java -p %JAVA_PACKAGE%

Solidity→Java変換

Batchファイルを実行します。

soltojava.bat

以下のような結果となれば変換が完了しています。

solc, the solidity compiler commandline interface
Version: 0.8.20+commit.a1b79de6.Windows.msvc
              _      _____ _ 
             | |    |____ (_)
__      _____| |__      / /_
\ \ /\ / / _ \ '_ \     \ \ |
 \ V  V /  __/ |_) |.___/ / |
  \_/\_/ \___|_.__/ \____/| |
                         _/ |
                        |__/
by Web3Labs
Version: 1.4.2
Build timestamp: 2022-10-21 08:18:21.49 UTC
Compiler run successful. Artifact(s) can be found in directory "./out".
              _      _____ _ 
             | |    |____ (_)
__      _____| |__      / /_
\ \ /\ / / _ \ '_ \     \ \ |
 \ V  V /  __/ |_) |.___/ / |
  \_/\_/ \___|_.__/ \____/| |
                         _/ |
                        |__/
by Web3Labs
Generating com.example.springhardhat.contracts.Token ... File written to .\src\main\java

src\main\java\com\example\springhardhat\contracts配下にToken.javaが生成されます。
大きなファイルなのでソースの掲載は割愛します。

テストコード作成

以下はテストコードです。

TokenTests.java
package com.example.springhardhat;

import static org.junit.jupiter.api.Assertions.assertEquals;

import java.math.BigInteger;

import org.junit.jupiter.api.Test;
import org.web3j.crypto.Credentials;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.gas.ContractGasProvider;
import org.web3j.tx.gas.StaticGasProvider;

import com.example.springhardhat.contracts.Token;

public class TokenTests {

    @Test
    void checkNameAndSymbol() throws Exception {
        // デプロイ
        String url = "http://127.0.0.1:8545/";
        Web3j web3j = Web3j.build(new HttpService(url));
        Credentials credentials = Credentials
                .create("0x0000000000000000000000000000000000000000000000000000000000000001");
        BigInteger gasPrice = BigInteger.valueOf(1000000000);
        BigInteger gasLimit = BigInteger.valueOf(30000000);
        ContractGasProvider contractGasProvider = new StaticGasProvider(gasPrice, gasLimit);
        // 名称とシンボル
        String name = "java test coin";
        String symbol = "jtc";
        Token token = Token.deploy(web3j, credentials, contractGasProvider, name, symbol).send();
        assertEquals(token.name().send(), name);
        assertEquals(token.symbol().send(), symbol);
    }

    @Test
    void checkMintAndBurn() throws Exception {
        // デプロイ
        String url = "http://127.0.0.1:8545/";
        Web3j web3j = Web3j.build(new HttpService(url));
        Credentials credentials = Credentials
                .create("0x0000000000000000000000000000000000000000000000000000000000000001");
        BigInteger gasPrice = BigInteger.valueOf(1000000000);
        BigInteger gasLimit = BigInteger.valueOf(30000000);
        ContractGasProvider contractGasProvider = new StaticGasProvider(gasPrice, gasLimit);
        String name = "java test coin";
        String symbol = "jtc";
        Token token = Token.deploy(web3j, credentials, contractGasProvider, name, symbol).send();
        String contractAddress = token.getContractAddress();
        // トークンの発行と破棄
        Token token2 = Token.load(contractAddress, web3j, credentials, contractGasProvider);
        Credentials credentials2 = Credentials
                .create("0x0000000000000000000000000000000000000000000000000000000000000002");
        BigInteger mintValue = BigInteger.valueOf(100000);
        BigInteger burnValue = BigInteger.valueOf(40000);
        BigInteger balanceOf1 = token2.balanceOf(credentials2.getAddress()).send();
        assertEquals(balanceOf1, BigInteger.ZERO);
        token2.mint(credentials2.getAddress(), mintValue).send();
        BigInteger balanceOf2 = token2.balanceOf(credentials2.getAddress()).send();
        assertEquals(balanceOf2, mintValue);
        token2.burn(credentials2.getAddress(), burnValue).send();
        BigInteger balanceOf3 = token2.balanceOf(credentials2.getAddress()).send();
        assertEquals(balanceOf3, mintValue.subtract(burnValue));
    }

}

テスト実行

JUnitでテストを実行する前に、以下のコマンドでノードを起動しておきます。

npx hardhat node

ノードが起動したら、テストを実行します。

まとめ

これで、Javaからいくらでもデプロイが出来ます。
が、秘密鍵をどう管理していくのか・・・そのあたりが重要なきもします。

今回のコードは以下のGithubにあります。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?