hshar

6 min read - Posted 14 Apr 19

Simple Kotlin SpringBoot dApp utilizing web3j

Are you sick of web3js? I know I am!

We are still forced to use it on the frontend but that doesn't mean that our backend event listeners and backend contract interactions have to go through this pile of poorly documented crap.

I want to introduce you to a great alternative that I've been using to write the backend of my dApps.

article imagearticle imagearticle image

First create a spring boot application: https://start.spring.io/

Pick Maven + Kotlin + latest version (I'm using 2.1.4) + you can leave the names as is or give it something fancy

Download and open that bad boy up in Intellij Idea. Community edition is free: https://www.jetbrains.com/idea/download

Now it should be pretty straight forward to get it running. Just browse over to the file (com.example.demo.DemoApplication) that contains @SpringBootApplication and there should be a little ▶ button. Press it? It won't do much now.

Let's leave this for now and go create our simple smart contract. I like using remix! https://remix.ethereum.org

Contract code (all it does is that it emits an event with the text we send it): ``` pragma solidity 0.5.7;

contract HelloKotlin {

function myTextInWeb3(string memory text) public {
    emit HelloKotlinEvent(
        text
    );
}

event HelloKotlinEvent(string text);

} `` Let's deploy it using remix. I deployed it here on rinkeby:0xde370152a5a35ebc1b575cf7feddb2e0e16a62a4`

Now let's compile it into a java class using web3j!

First download web3j command line tool: https://github.com/web3j/web3j/releases (I'm using web3j-4.2.0)

Second thing is to compile our contract into its abi and bin files. solc hellokotlin.sol --bin --abi --optimize -o . I'm using solcjs and it kind of messes up the names. Generating hellokotlinsolHelloKotlin.bin and hellokotlinsolHelloKotlin.abi

And let's generate the class: ~/Dev/web3j-4.2.0/bin/web3j solidity generate -b ./hellokotlin_sol_HelloKotlin.bin -a ./hellokotlin_sol_HelloKotlin.abi -o ~/Dev/demo/src/main/kotlin/ -p com.example.demo

Go back to IntelliJ and the generated class should be there and ready to be used! I'm going to rename it to something nicer: HelloKotlin.java (You can attempt to change this to use Kotlin but it strictly says to not modify the generated code. If you are successful let me know!)

Next is to add the web3j dependency to Maven. First open up pom.xml and add this inside of : <dependency> <groupId>org.web3j</groupId> <artifactId>core</artifactId> <version>4.2.0</version> </dependency> IntelliJ should prompt you to reimport. Do it!

Now my friends we can create some beans and event listeners:

  1. Create a kotlin class called Web3jConfig.kt ``` package com.example.demo

import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.web3j.protocol.Web3j import org.web3j.protocol.websocket.WebSocketService

@Configuration class Web3jConfig {

@Bean
fun web3jProvider(): Web3j {
    val wssService = WebSocketService(
            "wss://rinkeby.infura.io/ws",
            true
    )
    wssService.connect()
    return Web3j.build(wssService)
}

} ``` It just creates a bean of type Web3j that we can use to connect to Infura's WebSocket web3 provider!

Let's create another class to listen in on events and just printing them out to the console as they come in. Let's call this one EventListener.kt ``` package com.example.demo

import org.springframework.beans.factory.annotation.Autowired import org.springframework.context.annotation.Bean import org.springframework.stereotype.Service import org.web3j.protocol.Web3j import org.web3j.protocol.core.DefaultBlockParameterName import org.web3j.tx.ReadonlyTransactionManager import org.web3j.tx.gas.StaticGasProvider

@Service class EventListener { @Autowired lateinit var web3j: Web3j

companion object {
    const val CONTRACT_ADDRESS = "0xde370152a5a35ebc1b575cf7feddb2e0e16a62a4"
    const val GAS_PRICE = 200
    const val GAS_LIMIT = 4500000
}

@Bean
fun listen() {
    val transactionManager = ReadonlyTransactionManager(web3j, "0x0")
    val gasProvider = StaticGasProvider(GAS_PRICE.toBigInteger(), GAS_LIMIT.toBigInteger())

    val helloKotlinContract = HelloKotlin.load(
            CONTRACT_ADDRESS,
            web3j,
            transactionManager,
            gasProvider
    )

    helloKotlinContract.helloKotlinEventEventFlowable(
            DefaultBlockParameterName.EARLIEST,
            DefaultBlockParameterName.LATEST).subscribe { event ->
        println(event.text)
    }
}

} ```

Go back to the ▶ button and again, Press It! Now if you're using the contract at the address that I deployed then you'll see some text come in already. But to create your own text let's go back to remix and add in some text and fire up a transaction. If you look at the log of the application, you'll see something like this: ``` "C:\Program Files\Java\jdk-10.0.1\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2019.1\lib\idea_rt.jar=61097:C:\Program Files\JetBrains\IntelliJ IDEA Community Edition 2019.1\bin" -Dfile.encoding=UTF-8 -classpath C:\Users\hayde\Dev\demo\target\classes;C:\Users\hayde.m2\repository\org\springframework\boot\spring-boot-starter\2.1.4.RELEASE\spring-boot-starter-2.1.4.RELEASE.jar;C:\Users\hayde.m2\repository\org\springframework\boot\spring-boot\2.1.4.RELEASE\spring-boot-2.1.4.RELEASE.jar;C:\Users\hayde.m2\repository\org\springframework\spring-context\5.1.6.RELEASE\spring-context-5.1.6.RELEASE.jar;C:\Users\hayde.m2\repository\org\springframework\spring-aop\5.1.6.RELEASE\spring-aop-5.1.6.RELEASE.jar;C:\Users\hayde.m2\repository\org\springframework\spring-beans\5.1.6.RELEASE\spring-beans-5.1.6.RELEASE.jar;C:\Users\hayde.m2\repository\org\springframework\spring-expression\5.1.6.RELEASE\spring-expression-5.1.6.RELEASE.jar;C:\Users\hayde.m2\repository\org\springframework\boot\spring-boot-autoconfigure\2.1.4.RELEASE\spring-boot-autoconfigure-2.1.4.RELEASE.jar;C:\Users\hayde.m2\repository\org\springframework\boot\spring-boot-starter-logging\2.1.4.RELEASE\spring-boot-starter-logging-2.1.4.RELEASE.jar;C:\Users\hayde.m2\repository\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;C:\Users\hayde.m2\repository\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;C:\Users\hayde.m2\repository\org\apache\logging\log4j\log4j-to-slf4j\2.11.2\log4j-to-slf4j-2.11.2.jar;C:\Users\hayde.m2\repository\org\apache\logging\log4j\log4j-api\2.11.2\log4j-api-2.11.2.jar;C:\Users\hayde.m2\repository\org\slf4j\jul-to-slf4j\1.7.26\jul-to-slf4j-1.7.26.jar;C:\Users\hayde.m2\repository\javax\annotation\javax.annotation-api\1.3.2\javax.annotation-api-1.3.2.jar;C:\Users\hayde.m2\repository\org\springframework\spring-core\5.1.6.RELEASE\spring-core-5.1.6.RELEASE.jar;C:\Users\hayde.m2\repository\org\springframework\spring-jcl\5.1.6.RELEASE\spring-jcl-5.1.6.RELEASE.jar;C:\Users\hayde.m2\repository\org\yaml\snakeyaml\1.23\snakeyaml-1.23.jar;C:\Users\hayde.m2\repository\org\jetbrains\kotlin\kotlin-reflect\1.3.21\kotlin-reflect-1.3.21.jar;C:\Users\hayde.m2\repository\org\jetbrains\kotlin\kotlin-stdlib\1.3.21\kotlin-stdlib-1.3.21.jar;C:\Users\hayde.m2\repository\org\jetbrains\kotlin\kotlin-stdlib-common\1.3.21\kotlin-stdlib-common-1.3.21.jar;C:\Users\hayde.m2\repository\org\jetbrains\annotations\13.0\annotations-13.0.jar;C:\Users\hayde.m2\repository\org\jetbrains\kotlin\kotlin-stdlib-jdk8\1.3.21\kotlin-stdlib-jdk8-1.3.21.jar;C:\Users\hayde.m2\repository\org\jetbrains\kotlin\kotlin-stdlib-jdk7\1.3.21\kotlin-stdlib-jdk7-1.3.21.jar;C:\Users\hayde.m2\repository\org\web3j\core\4.2.0\core-4.2.0.jar;C:\Users\hayde.m2\repository\org\web3j\abi\4.2.0\abi-4.2.0.jar;C:\Users\hayde.m2\repository\org\web3j\utils\4.2.0\utils-4.2.0.jar;C:\Users\hayde.m2\repository\org\bouncycastle\bcprov-jdk15on\1.60\bcprov-jdk15on-1.60.jar;C:\Users\hayde.m2\repository\org\web3j\crypto\4.2.0\crypto-4.2.0.jar;C:\Users\hayde.m2\repository\org\web3j\rlp\4.2.0\rlp-4.2.0.jar;C:\Users\hayde.m2\repository\org\web3j\tuples\4.2.0\tuples-4.2.0.jar;C:\Users\hayde.m2\repository\com\github\jnr\jnr-unixsocket\0.21\jnr-unixsocket-0.21.jar;C:\Users\hayde.m2\repository\com\github\jnr\jnr-ffi\2.1.9\jnr-ffi-2.1.9.jar;C:\Users\hayde.m2\repository\com\github\jnr\jffi\1.2.17\jffi-1.2.17.jar;C:\Users\hayde.m2\repository\com\github\jnr\jffi\1.2.16\jffi-1.2.16-native.jar;C:\Users\hayde.m2\repository\org\ow2\asm\asm\5.0.3\asm-5.0.3.jar;C:\Users\hayde.m2\repository\org\ow2\asm\asm-commons\5.0.3\asm-commons-5.0.3.jar;C:\Users\hayde.m2\repository\org\ow2\asm\asm-analysis\5.0.3\asm-analysis-5.0.3.jar;C:\Users\hayde.m2\repository\org\ow2\asm\asm-tree\5.0.3\asm-tree-5.0.3.jar;C:\Users\hayde.m2\repository\org\ow2\asm\asm-util\5.0.3\asm-util-5.0.3.jar;C:\Users\hayde.m2\repository\com\github\jnr\jnr-a64asm\1.0.0\jnr-a64asm-1.0.0.jar;C:\Users\hayde.m2\repository\com\github\jnr\jnr-x86asm\1.0.2\jnr-x86asm-1.0.2.jar;C:\Users\hayde.m2\repository\com\github\jnr\jnr-constants\0.9.11\jnr-constants-0.9.11.jar;C:\Users\hayde.m2\repository\com\github\jnr\jnr-enxio\0.19\jnr-enxio-0.19.jar;C:\Users\hayde.m2\repository\com\github\jnr\jnr-posix\3.0.47\jnr-posix-3.0.47.jar;C:\Users\hayde.m2\repository\com\squareup\okhttp3\okhttp\3.8.1\okhttp-3.8.1.jar;C:\Users\hayde.m2\repository\com\squareup\okio\okio\1.13.0\okio-1.13.0.jar;C:\Users\hayde.m2\repository\com\squareup\okhttp3\logging-interceptor\3.8.1\logging-interceptor-3.8.1.jar;C:\Users\hayde.m2\repository\io\reactivex\rxjava2\rxjava\2.2.8\rxjava-2.2.8.jar;C:\Users\hayde.m2\repository\org\reactivestreams\reactive-streams\1.0.2\reactive-streams-1.0.2.jar;C:\Users\hayde.m2\repository\org\java-websocket\Java-WebSocket\1.3.8\Java-WebSocket-1.3.8.jar;C:\Users\hayde.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.9.8\jackson-databind-2.9.8.jar;C:\Users\hayde.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.9.0\jackson-annotations-2.9.0.jar;C:\Users\hayde.m2\repository\com\fasterxml\jackson\core\jackson-core\2.9.8\jackson-core-2.9.8.jar;C:\Users\hayde.m2\repository\org\slf4j\slf4j-api\1.7.26\slf4j-api-1.7.26.jar com.example.demo.DemoApplicationKt

. _ /\ / ' () _ \ \ \ \ ( ( )_ | ' | '| | ' / ` | \ \ \ \ \/ _)| |)| | | | | || (| | ) ) ) ) ' || .|| ||| |\, | / / / / =========||==============|/=///_/ :: Spring Boot :: (v2.1.4.RELEASE)

2019-04-14 00:55:55.256 INFO 16112 --- [ main] com.example.demo.DemoApplicationKt : Starting DemoApplicationKt on DESKTOP-2R74B1B with PID 16112 (C:\Users\hayde\Dev\demo\target\classes started by hayde in C:\Users\hayde\Dev\demo) 2019-04-14 00:55:55.258 INFO 16112 --- [ main] com.example.demo.DemoApplicationKt : No active profile set, falling back to default profiles: default 2019-04-14 00:55:58.718 INFO 16112 --- [ main] com.example.demo.DemoApplicationKt : Started DemoApplicationKt in 3.873 seconds (JVM running for 4.579) Hello There My Kotlin! ```

What do you think? A good alternative to web3js or not really? Please leave your feedback and ask me any questions! If this is received well I'll make a full dapp with a frontend that builds on this!

Also, am I doing something wrong? Please leave a comment for that as well :) I'm always looking to improve my Kotlin, Sprinboot, and Ethereum skills!

Created with Sketch.Content is"CC-BY-SA 4.0" licensed
Article On-chain
0 Comments
Related Articles
How To Achieve User Authentication By Web3 Wallet Using Spring Security, JWTs, Web3j & Kotlin

One of the greatest things that a web3 enabled browser gives us is saying goodbye to our primal ways of authenticating users with a username & password combination. If youre looking to authenticate users using a metamask or any web3 wallet then continue reading this article or simply explore the minimal code here: https://github.com/hshar7/springsecurity-web3-jwt-auth-demo You can checkout the code and follow along, or start fresh from spring initializer and start fresh, or add whatever you lear

How to Restart a Spring Application Context within a JUnit test

The Problem When attempting to write some failover tests to verify the resilience features in Eventeum, there didnt seem to be an easy way to restart a Spring application in the middle of an integration test. Spring provides the @DirtiesContext annotation in order to rebuild the context before or after a test run, but this is not of much use for service failure testing unless you write a suite of tests that depend on each other, which is generally regarded as a bad practice. The Solution After d