moecat

moecat

摸鱼大师

web3jの学習(1)

本文源コードは https://github.com/pigeon2049/planet-universal で確認できます。

Java のフルスタック開発者として、Solidity 言語は扱いが難しいように感じるので、まずは馴染みのある分野から始めます。

web3j

まずは従来の Spring Boot Maven プロジェクトを作成し、web3j 依存関係を追加します。

<dependency>
    <groupId>org.web3j</groupId>
    <artifactId>core</artifactId>
    <version>${web3j.version}</version>
</dependency>
<dependency>
    <groupId>org.web3j</groupId>
    <artifactId>contracts</artifactId>
    <version>${web3j.version}</version>
</dependency>

迅速な開発学習のために、ローカルに Docker をインストールする必要があります。

image

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-docker-compose</artifactId>
  <scope>runtime</scope>
  <optional>true</optional>
</dependency>

新しいバージョンの Spring Boot は Docker の統合をサポートしているため、環境をインストールするプロセスはなく、直接起動すると IPFS Kubo クライアントと InfluxDB 時系列データベースが得られます。


学習を開始します:
web3j は Ethereum クライアントノードを必要とします。自分で geth を使って構築することもできますが、Cloudflare が提供する公共ポートを利用することもできます。公共ポートを使用する際は、自分のアドレスや秘密鍵を使って契約とやり取りしないように注意してください。セキュリティ上の問題が発生しやすくなります。

application.properties に Cloudflare ETH ゲートウェイを追加します。

web3j.node.address=https://cloudflare-eth.com/

設定ファイルを定義し、web3j の Bean を生成します。

@Configuration
public class Web3jConfig {

    @Value("${web3j.node.address}")
    private String ethNode;

    @Bean
    public Web3j web3j() throws ExecutionException, InterruptedException {
        Web3j build = Web3j.build(new HttpService(ethNode));
        Web3ClientVersion web3ClientVersion = build.web3ClientVersion().sendAsync().get();
        System.out.println(web3ClientVersion.getWeb3ClientVersion());
        return build;
    }
}

これで、使用可能な web3j クライアントが得られました。


次に、Ethereum チェーン上のリアルタイム Gas 料金を取得する簡単なメソッドを書きます。

@Service
public class GasPrice {

    private final Web3j web3j;

    public GasPrice(Web3j web3j) {
        this.web3j = web3j;
    }

    public BigInteger gasGasPrice() throws IOException {
        return web3j.ethGasPrice().send().getGasPrice();
    }
}

簡単ですよね?

しかし、この単位が正しくないように感じるかもしれません。普段見るのは Gwei ですので、換算します。

BigInteger gas = gasPrice.gasGasPrice();
BigDecimal value= BigDecimal.valueOf(gas.longValue() * 0.000000001).setScale(2, RoundingMode.HALF_UP);

小数点以下 2 桁を保持すると、得られるのは Gwei です。

簡単な定期実行を設定し、毎秒 Gas データを取得してデータベースに保存します。

@Service
public class UpdateGasPrice implements CommandLineRunner {

    @Value("${influx.url}")
    String url;

    @Value("${influx.token}")
    String token;

    @Value("${influx.org}")
    String org;

    @Value("${influx.bucket}")
    String bucket;

    private final GasPrice gasPrice;

    public UpdateGasPrice(GasPrice gasPrice) {
        this.gasPrice = gasPrice;
    }

    @Override
    public void run(String... args) throws Exception {
        InfluxDBClient client = InfluxDBClientFactory.create(url, token.toCharArray(), org, bucket)
                .setLogLevel(LogLevel.BASIC);
        WriteApi writeApi = client.makeWriteApi(WriteOptions.builder().flushInterval(2_000).build());

        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                try {
                    BigInteger gas = gasPrice.gasGasPrice();
                    if (gas != null) {
                        BigDecimal value = BigDecimal.valueOf(gas.longValue() * 0.000000001).setScale(2, RoundingMode.HALF_UP);
                        Point point = Point
                                .measurement("eth")
                                .addField("gas", gas.longValue())
                                .addField("gasGwei", value)
                                .time(Instant.now(), WritePrecision.S);

                        System.out.println("Produced DataPoint: " + point.toLineProtocol());

                        writeApi.writePoint(point);
                    }
                } catch (IOException ignored) {}
            }
        }, 0, 1000);

        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            System.out.println("Close Client and Producer");
            timer.cancel();
            client.close();
        }));
    }
}

コードを実行します。

image

http://localhost:8086 を開き、InfluxDB の管理ページにアクセスします。パスワードは env に記載されています。

Gas データの書き込みが始まったことが確認できます。
image


アドレスの Ethereum 残高を取得します。

Ethereum チェーン上では非常に簡単です。

public BigDecimal getBalance(String address) throws IOException {
    BigInteger balance = web3j.ethGetBalance(address, DefaultBlockParameterName.LATEST).send().getBalance();
    BigDecimal ether = Convert.fromWei(String.valueOf(balance), Convert.Unit.ETHER);
    System.out.println(address + " balance:" + ether.toString());
    return ether;
}

ERC20 トークンの残高を取得します。

public BigDecimal getErc20Balance(String address, String tokenContractAddress) throws Exception {
    // 読み取り専用操作のためにReadonlyTransactionManagerを使用します
    ReadonlyTransactionManager txManager = new ReadonlyTransactionManager(web3j, null);
    DefaultGasProvider gasPriceProvider = new DefaultGasProvider();
    // ERC20は生成されたERC20契約ラッパークラスです
    ERC20 contract = ERC20.load(tokenContractAddress, web3j, txManager, gasPriceProvider);

    // ERC-20契約のbalanceOf関数を呼び出します
    BigInteger balance = contract.balanceOf(address).send();
    BigDecimal ether = Convert.fromWei(String.valueOf(balance), Convert.Unit.ETHER);
    System.out.println("Balance of " + address + ": " + ether);

    return ether;
}

今回の学習では簡易的なチェーン上の操作を行い、単純な読み取りを行いました。今後は契約とのインタラクションを深めて学習していきます。

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。