diff --git a/.editorconfig b/.editorconfig index 5b9451e..bd43bdc 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,14 +8,30 @@ root = true end_of_line = lf charset = utf-8 +# Json +[*.json] +indent_size = 2 +indent_style = space +insert_final_newline = false +trim_trailing_whitespace = true + # Yaml [{*.yml, *.yaml}] indent_size = 2 indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true # Property files [*.properties] indent_size = 2 indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true - +# XML files +[*.xml] +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/.gitattributes b/.gitattributes index ccc6fb5..856d969 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,9 +2,8 @@ # and leave all files detected as binary untouched. * text=auto -# + # The above will handle all files NOT found below -# # These files are text and should be normalized (Convert crlf => lf) *.bash text eol=lf *.css text diff=css @@ -26,16 +25,36 @@ *.xml text *.yml text eol=lf + # These files are binary and should be left untouched # (binary is a macro for -text -diff) -*.class binary +# Archives +*.7z binary +*.br binary +*.gz binary +*.tar binary +*.zip binary +*.jar binary +*.so binary +*.war binary *.dll binary -*.ear binary -*.gif binary + +# Documents +*.pdf binary + +# Images *.ico binary -*.jar binary +*.gif binary *.jpg binary *.jpeg binary *.png binary -*.so binary -*.war binary \ No newline at end of file +*.psd binary +*.webp binary + +# Fonts +*.woff2 binary + +# Other +*.exe binary +*.class binary +*.ear binary diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 613a39e..31c42f0 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - java: [ '11' ] + java: [ '11', '17' ] name: Java ${{ matrix.java }} setup steps: @@ -32,11 +32,19 @@ jobs: run: ./gradlew spotlessCheck - name: Test + if: matrix.java == '11' run: ./gradlew test jacocoTestReport env: - API_KEY: ${{ secrets.API_KEY }} + ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY_1 }} + + - name: Test + if: matrix.java == '17' + run: ./gradlew test jacocoTestReport + env: + ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY_2 }} - name: SonarQube + if: matrix.java == '17' run: ./gradlew sonarqube env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index c48c7a6..b56b41b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,18 @@ -/.settings/ -.idea -.idea/httpRequests -*.iml +### Package Files +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +### Gradle template .gradle -build +build/ target/ + +### Idea generated files +.idea +.settings/ +*.iml +out/ diff --git a/README.md b/README.md index c46a28f..dd244b5 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,20 @@ # Java EtherScan API +[![Minimum required Java version](https://img.shields.io/badge/Java-1.8%2B-blue?logo=openjdk)](https://openjdk.org/projects/jdk8/) [![GitHub Action](https://github.com/goodforgod/java-etherscan-api/workflows/Java%20CI/badge.svg)](https://github.com/GoodforGod/java-etherscan-api/actions?query=workflow%3A%22Java+CI%22) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=GoodforGod_java-etherscan-api&metric=coverage)](https://sonarcloud.io/dashboard?id=GoodforGod_java-etherscan-api) [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=GoodforGod_java-etherscan-api&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=GoodforGod_java-etherscan-api) [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=GoodforGod_java-etherscan-api&metric=ncloc)](https://sonarcloud.io/dashboard?id=GoodforGod_java-etherscan-api) -[![](https://jitpack.io/v/GoodforGod/java-etherscan-api.svg)](https://jitpack.io/#GoodforGod/java-etherscan-api) -[Etherscan.io](https://etherscan.io/apis) Java API implementation. +[Etherscan.io](https://docs.etherscan.io/) Java API implementation. -Library supports all available EtherScan *API* calls for all available *Ethereum Networks* for *etherscan.io* +Library supports EtherScan *API* for all available *Ethereum Networks* for *etherscan.io* ## Dependency :rocket: **Gradle** ```groovy -dependencies { - compile "com.github.goodforgod:java-etherscan-api:1.2.1" -} +implementation "com.github.goodforgod:java-etherscan-api:2.0.0" ``` **Maven** @@ -24,7 +22,7 @@ dependencies { com.github.goodforgod java-etherscan-api - 1.2.1 + 2.0.0 ``` @@ -42,143 +40,142 @@ dependencies { - [Token](#token-api) - [Version History](#version-history) -## Mainnet and Testnets +## MainNet and TestNets + +API support all Ethereum [default networks](https://docs.etherscan.io/getting-started/endpoint-urls): +- [Mainnet](https://api.etherscan.io/) +- [Goerli](https://api-goerli.etherscan.io/) +- [Sepolia](https://api-sepolia.etherscan.io/) -API support Ethereum: *[MAINNET](https://etherscan.io), - [ROPSTEN](https://ropsten.etherscan.io), - [KOVAN](https://kovan.etherscan.io), - [RINKEBY](https://rinkeby.etherscan.io), - [GORLI](https://goerli.etherscan.io), - [TOBALABA](https://tobalaba.etherscan.com)* networks. ```java -EtherScanApi api = new EtherScanApi(EthNetwork.MAINNET); // Default -EtherScanApi apiRinkeby = new EtherScanApi(EthNetwork.RINKEBY); -EtherScanApi apiRopsten = new EtherScanApi(EthNetwork.ROPSTEN); -EtherScanApi apiKovan = new EtherScanApi("YourApiKey", EthNetwork.KOVAN); +EtherScanAPI api = EtherScanAPI.build(); +EtherScanAPI apiGoerli = EtherScanAPI.builder().withNetwork(EthNetworks.GORLI).build(); +EtherScanAPI apiSepolia = EtherScanAPI.builder().withNetwork(EthNetworks.SEPOLIA).build(); +``` + +### Custom Network + +In case you want to use API for other EtherScan compatible network, you can easily provide custom network with domain api URI. + +```java +EtherScanAPI api = EtherScanAPI.builder() + .withNetwork(() -> URI.create("https://api-my-custom.etherscan.io/api")) + .build(); ``` ## Custom HttpClient In case you need to set custom timeout, custom headers or better implementation for HttpClient, -just implement **IHttpExecutor** by your self or initialize it with your values. +just implement **EthHttpClient** by your self or initialize it with your values. ```java -int connectionTimeout = 10000; -int readTimeout = 7000; - -Supplier supplier = () -> new HttpExecutor(connectionTimeout); -Supplier supplierFull = () -> new HttpExecutor(connectionTimeout, readTimeout); - -EtherScanApi api = new EtherScanApi(EthNetwork.RINKEBY, supplier); -EtherScanApi apiWithKey = new EtherScanApi("YourApiKey", EthNetwork.MAINNET, supplierFull); +Supplier ethHttpClientSupplier = () -> new UrlEthHttpClient(Duration.ofMillis(300), Duration.ofMillis(300)); +EtherScanAPI api = EtherScanAPI.builder() + .withHttpClient(supplier) + .build(); ``` ## API Examples -You can read about all API methods on [Etherscan](https://etherscan.io/apis) +You can read about all API methods on [Etherscan](https://docs.etherscan.io/api-endpoints/accounts) *Library support all available EtherScan API.* -You can use library *with or without* API key *([Check API request\sec restrictions when used without API key](https://ethereum.stackexchange.com/questions/34190/does-etherscan-require-the-use-of-an-api-key))*. +You can use library *with or without* API key *([Check API request\sec restrictions when used without API key](https://docs.etherscan.io/getting-started/viewing-api-usage-statistics))*. -Library will automatically limit requests up to **5 req/sec** when used *without* key. +Library will automatically limit requests up to **1 requests in 5 seconds** when used *without* key and up to **5 requests in 1 seconds** when used with API KEY (free plan). ```java -EtherScanApi api = new EtherScanApi(); -EtherScanApi api = new EtherScanApi("YourApiKey"); +EtherScanAPI.builder() + .withApiKey(ApiRunner.API_KEY) + .build(); ``` Below are examples for each API category. -### Account Api +### Account API **Get Ether Balance for a single Address** - ```java -EtherScanApi api = new EtherScanApi(); +EtherScanAPI api = EtherScanAPI.build(); Balance balance = api.account().balance("0x8d4426f94e42f721C7116E81d6688cd935cB3b4F"); ``` -### Block Api +### Block API **Get uncles block for block height** - ```java -EtherScanApi api = new EtherScanApi(); +EtherScanAPI api = EtherScanAPI.build(); Optional uncles = api.block().uncles(200000); ``` -### Contract Api +### Contract API **Request contract ABI from [verified codes](https://etherscan.io/contractsVerified)** ```java -EtherScanApi api = new EtherScanApi(); +EtherScanAPI api = EtherScanAPI.build(); Abi abi = api.contract().contractAbi("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413"); ``` -### Logs Api +### Logs API **Get event logs for single topic** - ```java -EtherScanApi api = new EtherScanApi(); -LogQuery query = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") +EtherScanAPI api = EtherScanAPI.build(); +LogQuery query = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") .build(); List logs = api.logs().logs(query); ``` **Get event logs for 3 topics with respectful operations** - ```java -EtherScanApi api = new EtherScanApi(); -LogQuery query = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - "0x72657075746174696f6e00000000000000000000000000000000000000000000", - "0x72657075746174696f6e00000000000000000000000000000000000000000000") +EtherScanAPI api = EtherScanAPI.build(); +LogQuery query = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withBlockFrom(379224) + .withBlockTo(400000) + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0x72657075746174696f6e00000000000000000000000000000000000000000000", + "0x72657075746174696f6e00000000000000000000000000000000000000000000") .setOpTopic0_1(LogOp.AND) - .setOpTopic0_2(LogOp.OR) + .setOpTopic0_2(null) .setOpTopic1_2(LogOp.AND) .build(); List logs = api.logs().logs(query); ``` -### Proxy Api - -**Get tx detailds with proxy endpoint** +### Proxy API +**Get tx details with proxy endpoint** ```java -EtherScanApi api = new EtherScanApi(EthNetwork.MAINNET); +EtherScanAPI api = EtherScanAPI.build(); Optional tx = api.proxy().tx("0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); ``` **Get block info with proxy endpoint** - ```java -EtherScanApi api = new EtherScanApi(EthNetwork.MAINNET); +EtherScanAPI api = EtherScanAPI.build(); Optional block = api.proxy().block(15215); ``` -### Stats Api +### Stats API **Statistic about last price** - ```java -EtherScanApi api = new EtherScanApi(); -Price price = api.stats().lastPrice(); +EtherScanAPI api = EtherScanAPI.build(); +Price price = api.stats().priceLast(); ``` -### Transaction Api +### Transaction API **Request receipt status for tx** - ```java -EtherScanApi api = new EtherScanApi(); +EtherScanAPI api = EtherScanAPI.build(); Optional status = api.txs().receiptStatus("0x513c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76"); ``` -### Token Api +### Token API -You can read about token API [here](https://etherscan.io/apis#tokens) +You can read about token API [here](https://docs.etherscan.io/api-endpoints/tokens) Token API methods migrated to [Account](#account-api) & [Stats](#stats-api) respectfully. diff --git a/build.gradle b/build.gradle index 70ed3fa..3d766c2 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { id "maven-publish" id "org.sonarqube" version "3.3" - id "com.diffplug.spotless" version "5.14.3" + id "com.diffplug.spotless" version "6.12.0" } repositories { @@ -18,34 +18,21 @@ version = artifactVersion sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 -spotless { - java { - encoding "UTF-8" - removeUnusedImports() - eclipse().configFile "${projectDir}/config/codestyle.xml" - } -} - -sonarqube { - properties { - property "sonar.host.url", "https://sonarcloud.io" - property "sonar.organization", "goodforgod" - property "sonar.projectKey", "GoodforGod_java-etherscan-api" - } -} - dependencies { - implementation "org.jetbrains:annotations:22.0.0" - implementation "com.google.code.gson:gson:2.8.9" + compileOnly "org.jetbrains:annotations:23.0.0" + implementation "io.goodforgod:gson-configuration:2.0.0" - testImplementation "junit:junit:4.13.2" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.9.3" + testImplementation "org.junit.jupiter:junit-jupiter-api:5.9.3" + testImplementation "org.junit.jupiter:junit-jupiter-params:5.9.3" } test { - useJUnit() + useJUnitPlatform() testLogging { events("passed", "skipped", "failed") exceptionFormat("full") + showStandardStreams(false) } reports { @@ -54,6 +41,23 @@ test { } } +spotless { + java { + encoding("UTF-8") + importOrder() + removeUnusedImports() + eclipse("4.21.0").configFile("${rootDir}/config/codestyle.xml") + } +} + +sonarqube { + properties { + property "sonar.host.url", "https://sonarcloud.io" + property "sonar.organization", "goodforgod" + property "sonar.projectKey", "GoodforGod_$artifactId" + } +} + publishing { publications { mavenJava(MavenPublication) { @@ -61,12 +65,12 @@ publishing { pom { name = "Java Etherscan API" - url = "https://github.com/GoodforGod/java-etherscan-api" + url = "https://github.com/GoodforGod/$artifactId" description = "Library is a wrapper for EtherScan API." license { name = "MIT License" - url = "https://github.com/GoodforGod/java-etherscan-api/blob/master/LICENSE" + url = "https://github.com/GoodforGod/$artifactId/blob/master/LICENSE" distribution = "repo" } @@ -78,9 +82,9 @@ publishing { } scm { - connection = "scm:git:git://github.com/GoodforGod/java-etherscan-api.git" - developerConnection = "scm:git:ssh://GoodforGod/java-etherscan-api.git" - url = "https://github.com/GoodforGod/java-etherscan-api/tree/master" + connection = "scm:git:git://github.com/GoodforGod/${artifactId}.git" + developerConnection = "scm:git:ssh://GoodforGod/${artifactId}.git" + url = "https://github.com/GoodforGod/$artifactId/tree/master" } } } diff --git a/config/codestyle.xml b/config/codestyle.xml index a90c4f5..ad0c929 100644 --- a/config/codestyle.xml +++ b/config/codestyle.xml @@ -1,156 +1,95 @@ - - + + - - - - - - - - - - - - - - + + - - - - + + - - - - - - - + + - - - - - - - - - - - - - - - - - + + - - - - + - - - + - - - - - - - - - + - + - - - - - - - - - - - - - + - - - + @@ -158,189 +97,292 @@ - - - - - - + + - - - + - - - - - - - - + - - - - - - - - - - - - - - + - - - - - + - - - - + - + - - - - - - - - + - + - - - - - - - - - + + - - - - - - - - - - - - - + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + - - - diff --git a/gradle.properties b/gradle.properties index a6ba485..821da06 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ groupId=com.github.goodforgod artifactId=java-etherscan-api -artifactVersion=1.2.1 +artifactVersion=2.0.0 ##### GRADLE ##### @@ -8,4 +8,9 @@ org.gradle.daemon=true org.gradle.parallel=true org.gradle.configureondemand=true org.gradle.caching=true -org.gradle.jvmargs=-Dfile.encoding=UTF-8 \ No newline at end of file +org.gradle.jvmargs=-Dfile.encoding=UTF-8 \ + --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \ + --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \ + --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \ + --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ + --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ffed3a2..070cb70 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 744e882..1b6c787 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,67 +17,101 @@ # ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +APP_BASE_NAME=${0##*/} # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MSYS* | MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -106,80 +140,95 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/src/main/java/io/api/etherscan/core/IAccountApi.java b/src/main/java/io/api/etherscan/core/IAccountApi.java deleted file mode 100644 index 25254aa..0000000 --- a/src/main/java/io/api/etherscan/core/IAccountApi.java +++ /dev/null @@ -1,140 +0,0 @@ -package io.api.etherscan.core; - -import io.api.etherscan.error.ApiException; -import io.api.etherscan.model.*; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - -/** - * EtherScan - API Descriptions https://etherscan.io/apis#accounts - * - * @author GoodforGod - * @since 28.10.2018 - */ -public interface IAccountApi { - - /** - * Address ETH balance - * - * @param address get balance for - * @return balance - * @throws ApiException parent exception class - */ - @NotNull - Balance balance(String address) throws ApiException; - - /** - * ERC20 token balance for address - * - * @param address get balance for - * @param contract token contract - * @return token balance for address - * @throws ApiException parent exception class - */ - @NotNull - TokenBalance balance(String address, String contract) throws ApiException; - - /** - * Maximum 20 address for single batch request If address MORE THAN 20, then there will be more than - * 1 request performed - * - * @param addresses addresses to get balances for - * @return list of balances - * @throws ApiException parent exception class - */ - @NotNull - List balances(List addresses) throws ApiException; - - /** - * All txs for given address - * - * @param address get txs for - * @param startBlock tx from this blockNumber - * @param endBlock tx to this blockNumber - * @return txs for address - * @throws ApiException parent exception class - */ - @NotNull - List txs(String address, long startBlock, long endBlock) throws ApiException; - - @NotNull - List txs(String address, long startBlock) throws ApiException; - - @NotNull - List txs(String address) throws ApiException; - - /** - * All internal txs for given address - * - * @param address get txs for - * @param startBlock tx from this blockNumber - * @param endBlock tx to this blockNumber - * @return txs for address - * @throws ApiException parent exception class - */ - @NotNull - List txsInternal(String address, long startBlock, long endBlock) throws ApiException; - - @NotNull - List txsInternal(String address, long startBlock) throws ApiException; - - @NotNull - List txsInternal(String address) throws ApiException; - - /** - * All internal tx for given transaction hash - * - * @param txhash transaction hash - * @return internal txs list - * @throws ApiException parent exception class - */ - @NotNull - List txsInternalByHash(String txhash) throws ApiException; - - /** - * All ERC-20 token txs for given address - * - * @param address get txs for - * @param startBlock tx from this blockNumber - * @param endBlock tx to this blockNumber - * @return txs for address - * @throws ApiException parent exception class - */ - @NotNull - List txsToken(String address, long startBlock, long endBlock) throws ApiException; - - @NotNull - List txsToken(String address, long startBlock) throws ApiException; - - @NotNull - List txsToken(String address) throws ApiException; - - /** - * All ERC-721 (NFT) token txs for given address - * - * @param address get txs for - * @param startBlock tx from this blockNumber - * @param endBlock tx to this blockNumber - * @return txs for address - * @throws ApiException parent exception class - */ - @NotNull - List txsNftToken(String address, long startBlock, long endBlock) throws ApiException; - - @NotNull - List txsNftToken(String address, long startBlock) throws ApiException; - - @NotNull - List txsNftToken(String address) throws ApiException; - - /** - * All blocks mined by address - * - * @param address address to search for - * @return blocks mined - * @throws ApiException parent exception class - */ - @NotNull - List minedBlocks(String address) throws ApiException; -} diff --git a/src/main/java/io/api/etherscan/core/IBlockApi.java b/src/main/java/io/api/etherscan/core/IBlockApi.java deleted file mode 100644 index 7381ac0..0000000 --- a/src/main/java/io/api/etherscan/core/IBlockApi.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.api.etherscan.core; - -import io.api.etherscan.error.ApiException; -import io.api.etherscan.model.UncleBlock; -import org.jetbrains.annotations.NotNull; - -import java.util.Optional; - -/** - * EtherScan - API Descriptions https://etherscan.io/apis#blocks - * - * @author GoodforGod - * @since 30.10.2018 - */ -public interface IBlockApi { - - /** - * Return uncle blocks - * - * @param blockNumber block number form 0 to last - * @return optional uncle blocks - * @throws ApiException parent exception class - */ - @NotNull - Optional uncles(long blockNumber) throws ApiException; -} diff --git a/src/main/java/io/api/etherscan/core/IContractApi.java b/src/main/java/io/api/etherscan/core/IContractApi.java deleted file mode 100644 index 3e9388d..0000000 --- a/src/main/java/io/api/etherscan/core/IContractApi.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.api.etherscan.core; - -import io.api.etherscan.error.ApiException; -import io.api.etherscan.model.Abi; -import org.jetbrains.annotations.NotNull; - -/** - * EtherScan - API Descriptions https://etherscan.io/apis#contracts - * - * @author GoodforGod - * @since 28.10.2018 - */ -public interface IContractApi { - - /** - * Get Verified Contract Sources - * - * @param address to verify - * @return ABI verified - * @throws ApiException parent exception class - */ - @NotNull - Abi contractAbi(String address) throws ApiException; -} diff --git a/src/main/java/io/api/etherscan/core/ILogsApi.java b/src/main/java/io/api/etherscan/core/ILogsApi.java deleted file mode 100644 index 37c5eac..0000000 --- a/src/main/java/io/api/etherscan/core/ILogsApi.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.api.etherscan.core; - -import io.api.etherscan.error.ApiException; -import io.api.etherscan.model.Log; -import io.api.etherscan.model.query.impl.LogQuery; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - -/** - * EtherScan - API Descriptions https://etherscan.io/apis#logs - * - * @author GoodforGod - * @since 30.10.2018 - */ -public interface ILogsApi { - - /** - * alternative to the native eth_getLogs Read at EtherScan API description for full info! - * - * @param query build log query - * @return logs according to query - * @throws ApiException parent exception class - * - * @see io.api.etherscan.model.query.impl.LogQueryBuilder - */ - @NotNull - List logs(LogQuery query) throws ApiException; -} diff --git a/src/main/java/io/api/etherscan/core/IStatisticApi.java b/src/main/java/io/api/etherscan/core/IStatisticApi.java deleted file mode 100644 index 1b7ef59..0000000 --- a/src/main/java/io/api/etherscan/core/IStatisticApi.java +++ /dev/null @@ -1,45 +0,0 @@ -package io.api.etherscan.core; - -import io.api.etherscan.error.ApiException; -import io.api.etherscan.model.Price; -import io.api.etherscan.model.Supply; -import org.jetbrains.annotations.NotNull; - -import java.math.BigInteger; - -/** - * EtherScan - API Descriptions https://etherscan.io/apis#stats - * - * @author GoodforGod - * @since 30.10.2018 - */ -public interface IStatisticApi { - - /** - * ERC20 token total Supply - * - * @param contract contract address - * @return token supply for specified contract - * @throws ApiException parent exception class - */ - @NotNull - BigInteger supply(String contract) throws ApiException; - - /** - * Eth total supply - * - * @return total ETH supply for moment - * @throws ApiException parent exception class - */ - @NotNull - Supply supply() throws ApiException; - - /** - * Eth last USD and BTC price - * - * @return last usd/btc price for ETH - * @throws ApiException parent exception class - */ - @NotNull - Price lastPrice() throws ApiException; -} diff --git a/src/main/java/io/api/etherscan/core/impl/AccountApiProvider.java b/src/main/java/io/api/etherscan/core/impl/AccountApiProvider.java deleted file mode 100644 index 77d8b88..0000000 --- a/src/main/java/io/api/etherscan/core/impl/AccountApiProvider.java +++ /dev/null @@ -1,268 +0,0 @@ -package io.api.etherscan.core.impl; - -import io.api.etherscan.core.IAccountApi; -import io.api.etherscan.error.ApiException; -import io.api.etherscan.error.EtherScanException; -import io.api.etherscan.executor.IHttpExecutor; -import io.api.etherscan.manager.IQueueManager; -import io.api.etherscan.model.*; -import io.api.etherscan.model.utility.*; -import io.api.etherscan.util.BasicUtils; -import org.jetbrains.annotations.NotNull; - -import java.math.BigInteger; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -/** - * Account API Implementation - * - * @see IAccountApi - * - * @author GoodforGod - * @since 28.10.2018 - */ -public class AccountApiProvider extends BasicProvider implements IAccountApi { - - private static final int OFFSET_MAX = 10000; - - private static final String ACT_BALANCE_ACTION = ACT_PREFIX + "balance"; - private static final String ACT_TOKEN_BALANCE_PARAM = ACT_PREFIX + "tokenbalance"; - private static final String ACT_BALANCE_MULTI_ACTION = ACT_PREFIX + "balancemulti"; - private static final String ACT_TX_ACTION = ACT_PREFIX + "txlist"; - private static final String ACT_TX_INTERNAL_ACTION = ACT_PREFIX + "txlistinternal"; - private static final String ACT_TX_TOKEN_ACTION = ACT_PREFIX + "tokentx"; - private static final String ACT_TX_NFT_TOKEN_ACTION = ACT_PREFIX + "tokennfttx"; - private static final String ACT_MINED_ACTION = ACT_PREFIX + "getminedblocks"; - - private static final String BLOCK_TYPE_PARAM = "&blocktype=blocks"; - private static final String CONTRACT_PARAM = "&contractaddress="; - private static final String START_BLOCK_PARAM = "&startblock="; - private static final String TAG_LATEST_PARAM = "&tag=latest"; - private static final String END_BLOCK_PARAM = "&endblock="; - private static final String SORT_DESC_PARAM = "&sort=desc"; - private static final String SORT_ASC_PARAM = "&sort=asc"; - private static final String ADDRESS_PARAM = "&address="; - private static final String TXHASH_PARAM = "&txhash="; - private static final String OFFSET_PARAM = "&offset="; - private static final String PAGE_PARAM = "&page="; - - AccountApiProvider(final IQueueManager queueManager, - final String baseUrl, - final IHttpExecutor executor) { - super(queueManager, "account", baseUrl, executor); - } - - @NotNull - @Override - public Balance balance(final String address) throws ApiException { - BasicUtils.validateAddress(address); - - final String urlParams = ACT_BALANCE_ACTION + TAG_LATEST_PARAM + ADDRESS_PARAM + address; - final StringResponseTO response = getRequest(urlParams, StringResponseTO.class); - if (response.getStatus() != 1) - throw new EtherScanException(response); - - return new Balance(address, new BigInteger(response.getResult())); - } - - @NotNull - @Override - public TokenBalance balance(final String address, final String contract) throws ApiException { - BasicUtils.validateAddress(address); - BasicUtils.validateAddress(contract); - - final String urlParams = ACT_TOKEN_BALANCE_PARAM + ADDRESS_PARAM + address + CONTRACT_PARAM + contract; - final StringResponseTO response = getRequest(urlParams, StringResponseTO.class); - if (response.getStatus() != 1) - throw new EtherScanException(response); - - return new TokenBalance(address, new BigInteger(response.getResult()), contract); - } - - @NotNull - @Override - public List balances(final List addresses) throws ApiException { - if (BasicUtils.isEmpty(addresses)) - return Collections.emptyList(); - - BasicUtils.validateAddresses(addresses); - - // Maximum addresses in batch request - 20 - final List balances = new ArrayList<>(); - final List> addressesAsBatches = BasicUtils.partition(addresses, 20); - - for (final List batch : addressesAsBatches) { - final String urlParams = ACT_BALANCE_MULTI_ACTION + TAG_LATEST_PARAM + ADDRESS_PARAM + toAddressParam(batch); - final BalanceResponseTO response = getRequest(urlParams, BalanceResponseTO.class); - if (response.getStatus() != 1) - throw new EtherScanException(response); - - if (!BasicUtils.isEmpty(response.getResult())) - balances.addAll(response.getResult().stream() - .map(Balance::of) - .collect(Collectors.toList())); - } - - return balances; - } - - private String toAddressParam(final List addresses) { - return addresses.stream().collect(Collectors.joining(",")); - } - - @NotNull - @Override - public List txs(final String address) throws ApiException { - return txs(address, MIN_START_BLOCK); - } - - @NotNull - @Override - public List txs(final String address, final long startBlock) throws ApiException { - return txs(address, startBlock, MAX_END_BLOCK); - } - - @NotNull - @Override - public List txs(final String address, final long startBlock, final long endBlock) throws ApiException { - BasicUtils.validateAddress(address); - final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); - - final String offsetParam = PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX; - final String blockParam = START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end(); - final String urlParams = ACT_TX_ACTION + offsetParam + ADDRESS_PARAM + address + blockParam + SORT_ASC_PARAM; - - return getRequestUsingOffset(urlParams, TxResponseTO.class); - } - - /** - * Generic search for txs using offset api param To avoid 10k limit per response - * - * @param urlParams Url params for #getRequest() - * @param tClass responseListTO class - * @param responseTO list T type - * @param responseListTO type - * @return List of T values - */ - private List getRequestUsingOffset(final String urlParams, - Class tClass) throws ApiException { - final List result = new ArrayList<>(); - int page = 1; - while (true) { - final String formattedUrl = String.format(urlParams, page++); - final R response = getRequest(formattedUrl, tClass); - BasicUtils.validateTxResponse(response); - if (BasicUtils.isEmpty(response.getResult())) - break; - - result.addAll(response.getResult()); - if (response.getResult().size() < OFFSET_MAX) - break; - } - - return result; - } - - @NotNull - @Override - public List txsInternal(final String address) throws ApiException { - return txsInternal(address, MIN_START_BLOCK); - } - - @NotNull - @Override - public List txsInternal(final String address, final long startBlock) throws ApiException { - return txsInternal(address, startBlock, MAX_END_BLOCK); - } - - @NotNull - @Override - public List txsInternal(final String address, final long startBlock, final long endBlock) throws ApiException { - BasicUtils.validateAddress(address); - final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); - - final String offsetParam = PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX; - final String blockParam = START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end(); - final String urlParams = ACT_TX_INTERNAL_ACTION + offsetParam + ADDRESS_PARAM + address + blockParam + SORT_ASC_PARAM; - - return getRequestUsingOffset(urlParams, TxInternalResponseTO.class); - } - - @NotNull - @Override - public List txsInternalByHash(final String txhash) throws ApiException { - BasicUtils.validateTxHash(txhash); - - final String urlParams = ACT_TX_INTERNAL_ACTION + TXHASH_PARAM + txhash; - final TxInternalResponseTO response = getRequest(urlParams, TxInternalResponseTO.class); - BasicUtils.validateTxResponse(response); - - return BasicUtils.isEmpty(response.getResult()) - ? Collections.emptyList() - : response.getResult(); - } - - @NotNull - @Override - public List txsToken(final String address) throws ApiException { - return txsToken(address, MIN_START_BLOCK); - } - - @NotNull - @Override - public List txsToken(final String address, final long startBlock) throws ApiException { - return txsToken(address, startBlock, MAX_END_BLOCK); - } - - @NotNull - @Override - public List txsToken(final String address, final long startBlock, final long endBlock) throws ApiException { - BasicUtils.validateAddress(address); - final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); - - final String offsetParam = PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX; - final String blockParam = START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end(); - final String urlParams = ACT_TX_TOKEN_ACTION + offsetParam + ADDRESS_PARAM + address + blockParam + SORT_ASC_PARAM; - - return getRequestUsingOffset(urlParams, TxTokenResponseTO.class); - } - - @NotNull - @Override - public List txsNftToken(String address) throws ApiException { - return txsNftToken(address, MIN_START_BLOCK); - } - - @NotNull - @Override - public List txsNftToken(String address, long startBlock) throws ApiException { - return txsNftToken(address, startBlock, MAX_END_BLOCK); - } - - @NotNull - @Override - public List txsNftToken(String address, long startBlock, long endBlock) throws ApiException { - BasicUtils.validateAddress(address); - final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); - - final String offsetParam = PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX; - final String blockParam = START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end(); - final String urlParams = ACT_TX_NFT_TOKEN_ACTION + offsetParam + ADDRESS_PARAM + address + blockParam + SORT_ASC_PARAM; - - return getRequestUsingOffset(urlParams, TxTokenResponseTO.class); - } - - @NotNull - @Override - public List minedBlocks(final String address) throws ApiException { - BasicUtils.validateAddress(address); - - final String offsetParam = PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX; - final String urlParams = ACT_MINED_ACTION + offsetParam + BLOCK_TYPE_PARAM + ADDRESS_PARAM + address; - - return getRequestUsingOffset(urlParams, BlockResponseTO.class); - } -} diff --git a/src/main/java/io/api/etherscan/core/impl/BasicProvider.java b/src/main/java/io/api/etherscan/core/impl/BasicProvider.java deleted file mode 100644 index b36f406..0000000 --- a/src/main/java/io/api/etherscan/core/impl/BasicProvider.java +++ /dev/null @@ -1,100 +0,0 @@ -package io.api.etherscan.core.impl; - -import com.google.gson.*; -import io.api.etherscan.error.ApiException; -import io.api.etherscan.error.EtherScanException; -import io.api.etherscan.error.ParseException; -import io.api.etherscan.error.RateLimitException; -import io.api.etherscan.executor.IHttpExecutor; -import io.api.etherscan.manager.IQueueManager; -import io.api.etherscan.model.utility.StringResponseTO; -import io.api.etherscan.util.BasicUtils; - -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.util.Map; - -/** - * Base provider for API Implementations - * - * @author GoodforGod - * @see EtherScanApi - * @since 28.10.2018 - */ -abstract class BasicProvider { - - static final int MAX_END_BLOCK = Integer.MAX_VALUE; - static final int MIN_START_BLOCK = 0; - - static final String ACT_PREFIX = "&action="; - - private final String module; - private final String baseUrl; - private final IHttpExecutor executor; - private final IQueueManager queue; - private final Gson gson; - - BasicProvider(final IQueueManager queue, - final String module, - final String baseUrl, - final IHttpExecutor executor) { - this.queue = queue; - this.module = "&module=" + module; - this.baseUrl = baseUrl; - this.executor = executor; - this.gson = new GsonBuilder() - .registerTypeAdapter(LocalDateTime.class, (JsonSerializer) (src, t, c) -> new JsonPrimitive("")) - .registerTypeAdapter(LocalDate.class, (JsonSerializer) (src, t, context) -> new JsonPrimitive("")) - .registerTypeAdapter(LocalDateTime.class, (JsonDeserializer) (json, t, c) -> null) - .registerTypeAdapter(LocalDate.class, (JsonDeserializer) (json, t, c) -> null) - .create(); - } - - T convert(final String json, final Class tClass) { - try { - final T t = gson.fromJson(json, tClass); - if (t instanceof StringResponseTO && ((StringResponseTO) t).getResult().startsWith("Max rate limit reached")) { - throw new RateLimitException(((StringResponseTO) t).getResult()); - } - - return t; - } catch (Exception e) { - try { - final Map map = gson.fromJson(json, Map.class); - final Object result = map.get("result"); - if (result instanceof String && ((String) result).startsWith("Max rate limit reached")) - throw new RateLimitException(((String) result)); - - throw new ParseException(e.getMessage() + ", for response: " + json, e.getCause(), json); - } catch (ApiException ex) { - throw ex; - } catch (Exception ex) { - throw new ParseException(e.getMessage() + ", for response: " + json, e.getCause(), json); - } - } - } - - String getRequest(final String urlParameters) { - queue.takeTurn(); - final String url = baseUrl + module + urlParameters; - final String result = executor.get(url); - if (BasicUtils.isEmpty(result)) - throw new EtherScanException("Server returned null value for GET request at URL - " + url); - - return result; - } - - String postRequest(final String urlParameters, final String dataToPost) { - queue.takeTurn(); - final String url = baseUrl + module + urlParameters; - return executor.post(url, dataToPost); - } - - T getRequest(final String urlParameters, final Class tClass) { - return convert(getRequest(urlParameters), tClass); - } - - T postRequest(final String urlParameters, final String dataToPost, final Class tClass) { - return convert(postRequest(urlParameters, dataToPost), tClass); - } -} diff --git a/src/main/java/io/api/etherscan/core/impl/BlockApiProvider.java b/src/main/java/io/api/etherscan/core/impl/BlockApiProvider.java deleted file mode 100644 index 9f386a7..0000000 --- a/src/main/java/io/api/etherscan/core/impl/BlockApiProvider.java +++ /dev/null @@ -1,48 +0,0 @@ -package io.api.etherscan.core.impl; - -import io.api.etherscan.core.IBlockApi; -import io.api.etherscan.error.ApiException; -import io.api.etherscan.executor.IHttpExecutor; -import io.api.etherscan.manager.IQueueManager; -import io.api.etherscan.model.UncleBlock; -import io.api.etherscan.model.utility.UncleBlockResponseTO; -import io.api.etherscan.util.BasicUtils; -import org.jetbrains.annotations.NotNull; - -import java.util.Optional; - -/** - * Block API Implementation - * - * @see IBlockApi - * - * @author GoodforGod - * @since 28.10.2018 - */ -public class BlockApiProvider extends BasicProvider implements IBlockApi { - - private static final String ACT_BLOCK_PARAM = ACT_PREFIX + "getblockreward"; - - private static final String BLOCKNO_PARAM = "&blockno="; - - BlockApiProvider(final IQueueManager queueManager, - final String baseUrl, - final IHttpExecutor executor) { - super(queueManager, "block", baseUrl, executor); - } - - @NotNull - @Override - public Optional uncles(long blockNumber) throws ApiException { - final String urlParam = ACT_BLOCK_PARAM + BLOCKNO_PARAM + blockNumber; - final String response = getRequest(urlParam); - if (BasicUtils.isEmpty(response) || response.contains("NOTOK")) - return Optional.empty(); - - final UncleBlockResponseTO responseTO = convert(response, UncleBlockResponseTO.class); - BasicUtils.validateTxResponse(responseTO); - return (responseTO.getResult() == null || responseTO.getResult().isEmpty()) - ? Optional.empty() - : Optional.of(responseTO.getResult()); - } -} diff --git a/src/main/java/io/api/etherscan/core/impl/ContractApiProvider.java b/src/main/java/io/api/etherscan/core/impl/ContractApiProvider.java deleted file mode 100644 index 125087f..0000000 --- a/src/main/java/io/api/etherscan/core/impl/ContractApiProvider.java +++ /dev/null @@ -1,47 +0,0 @@ -package io.api.etherscan.core.impl; - -import io.api.etherscan.core.IContractApi; -import io.api.etherscan.error.ApiException; -import io.api.etherscan.error.EtherScanException; -import io.api.etherscan.executor.IHttpExecutor; -import io.api.etherscan.manager.IQueueManager; -import io.api.etherscan.model.Abi; -import io.api.etherscan.model.utility.StringResponseTO; -import io.api.etherscan.util.BasicUtils; -import org.jetbrains.annotations.NotNull; - -/** - * Contract API Implementation - * - * @see IContractApi - * - * @author GoodforGod - * @since 28.10.2018 - */ -public class ContractApiProvider extends BasicProvider implements IContractApi { - - private static final String ACT_ABI_PARAM = ACT_PREFIX + "getabi"; - - private static final String ADDRESS_PARAM = "&address="; - - ContractApiProvider(final IQueueManager queueManager, - final String baseUrl, - final IHttpExecutor executor) { - super(queueManager, "contract", baseUrl, executor); - } - - @NotNull - @Override - public Abi contractAbi(final String address) throws ApiException { - BasicUtils.validateAddress(address); - - final String urlParam = ACT_ABI_PARAM + ADDRESS_PARAM + address; - final StringResponseTO response = getRequest(urlParam, StringResponseTO.class); - if (response.getStatus() != 1 && "NOTOK".equals(response.getMessage())) - throw new EtherScanException(response); - - return (response.getResult().startsWith("Contract sou")) - ? Abi.nonVerified() - : Abi.verified(response.getResult()); - } -} diff --git a/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java b/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java deleted file mode 100644 index ba5dd83..0000000 --- a/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java +++ /dev/null @@ -1,140 +0,0 @@ -package io.api.etherscan.core.impl; - -import io.api.etherscan.core.*; -import io.api.etherscan.error.ApiException; -import io.api.etherscan.error.ApiKeyException; -import io.api.etherscan.executor.IHttpExecutor; -import io.api.etherscan.executor.impl.HttpExecutor; -import io.api.etherscan.manager.IQueueManager; -import io.api.etherscan.manager.impl.FakeQueueManager; -import io.api.etherscan.manager.impl.QueueManager; -import io.api.etherscan.model.EthNetwork; -import io.api.etherscan.util.BasicUtils; -import org.jetbrains.annotations.NotNull; - -import java.util.function.Supplier; - -/** - * EtherScan full API Description https://etherscan.io/apis - * - * @author GoodforGod - * @since 28.10.2018 - */ -public class EtherScanApi implements AutoCloseable { - - private static final Supplier DEFAULT_SUPPLIER = HttpExecutor::new; - - public static final String DEFAULT_KEY = "YourApiKeyToken"; - - private final IQueueManager queueManager; - private final IAccountApi account; - private final IBlockApi block; - private final IContractApi contract; - private final ILogsApi logs; - private final IProxyApi proxy; - private final IStatisticApi stats; - private final ITransactionApi txs; - - public EtherScanApi() { - this(DEFAULT_KEY, EthNetwork.MAINNET); - } - - public EtherScanApi(final EthNetwork network) { - this(DEFAULT_KEY, network); - } - - public EtherScanApi(final String apiKey) { - this(apiKey, EthNetwork.MAINNET); - } - - public EtherScanApi(final EthNetwork network, - final Supplier executorSupplier) { - this(DEFAULT_KEY, network, executorSupplier); - } - - public EtherScanApi(final String apiKey, - final EthNetwork network, - final IQueueManager queue) { - this(apiKey, network, DEFAULT_SUPPLIER, queue); - } - - public EtherScanApi(final String apiKey, - final EthNetwork network) { - this(apiKey, network, DEFAULT_SUPPLIER); - } - - public EtherScanApi(final String apiKey, - final EthNetwork network, - final Supplier executorSupplier) { - this(apiKey, network, executorSupplier, - DEFAULT_KEY.equals(apiKey) - ? QueueManager.DEFAULT_KEY_QUEUE - : new FakeQueueManager()); - } - - public EtherScanApi(final String apiKey, - final EthNetwork network, - final Supplier executorSupplier, - final IQueueManager queue) { - if (BasicUtils.isBlank(apiKey)) - throw new ApiKeyException("API key can not be null or empty"); - - if (network == null) - throw new ApiException("Ethereum Network is set to NULL value"); - - // EtherScan 1request\5sec limit support by queue manager - final IHttpExecutor executor = executorSupplier.get(); - - final String ending = EthNetwork.TOBALABA.equals(network) ? "com" : "io"; - final String baseUrl = "https://" + network.getDomain() + ".etherscan." + ending + "/api" + "?apikey=" + apiKey; - - this.queueManager = queue; - this.account = new AccountApiProvider(queue, baseUrl, executor); - this.block = new BlockApiProvider(queue, baseUrl, executor); - this.contract = new ContractApiProvider(queue, baseUrl, executor); - this.logs = new LogsApiProvider(queue, baseUrl, executor); - this.proxy = new ProxyApiProvider(queue, baseUrl, executor); - this.stats = new StatisticApiProvider(queue, baseUrl, executor); - this.txs = new TransactionApiProvider(queue, baseUrl, executor); - } - - @NotNull - public IAccountApi account() { - return account; - } - - @NotNull - public IContractApi contract() { - return contract; - } - - @NotNull - public ITransactionApi txs() { - return txs; - } - - @NotNull - public IBlockApi block() { - return block; - } - - @NotNull - public ILogsApi logs() { - return logs; - } - - @NotNull - public IProxyApi proxy() { - return proxy; - } - - @NotNull - public IStatisticApi stats() { - return stats; - } - - @Override - public void close() throws Exception { - queueManager.close(); - } -} diff --git a/src/main/java/io/api/etherscan/core/impl/LogsApiProvider.java b/src/main/java/io/api/etherscan/core/impl/LogsApiProvider.java deleted file mode 100644 index 6086869..0000000 --- a/src/main/java/io/api/etherscan/core/impl/LogsApiProvider.java +++ /dev/null @@ -1,45 +0,0 @@ -package io.api.etherscan.core.impl; - -import io.api.etherscan.core.ILogsApi; -import io.api.etherscan.error.ApiException; -import io.api.etherscan.executor.IHttpExecutor; -import io.api.etherscan.manager.IQueueManager; -import io.api.etherscan.model.Log; -import io.api.etherscan.model.query.impl.LogQuery; -import io.api.etherscan.model.utility.LogResponseTO; -import io.api.etherscan.util.BasicUtils; -import org.jetbrains.annotations.NotNull; - -import java.util.Collections; -import java.util.List; - -/** - * Logs API Implementation - * - * @see ILogsApi - * - * @author GoodforGod - * @since 28.10.2018 - */ -public class LogsApiProvider extends BasicProvider implements ILogsApi { - - private static final String ACT_LOGS_PARAM = ACT_PREFIX + "getLogs"; - - LogsApiProvider(final IQueueManager queue, - final String baseUrl, - final IHttpExecutor executor) { - super(queue, "logs", baseUrl, executor); - } - - @NotNull - @Override - public List logs(final LogQuery query) throws ApiException { - final String urlParams = ACT_LOGS_PARAM + query.getParams(); - final LogResponseTO response = getRequest(urlParams, LogResponseTO.class); - BasicUtils.validateTxResponse(response); - - return (BasicUtils.isEmpty(response.getResult())) - ? Collections.emptyList() - : response.getResult(); - } -} diff --git a/src/main/java/io/api/etherscan/core/impl/StatisticApiProvider.java b/src/main/java/io/api/etherscan/core/impl/StatisticApiProvider.java deleted file mode 100644 index d178a81..0000000 --- a/src/main/java/io/api/etherscan/core/impl/StatisticApiProvider.java +++ /dev/null @@ -1,71 +0,0 @@ -package io.api.etherscan.core.impl; - -import io.api.etherscan.core.IStatisticApi; -import io.api.etherscan.error.ApiException; -import io.api.etherscan.error.EtherScanException; -import io.api.etherscan.executor.IHttpExecutor; -import io.api.etherscan.manager.IQueueManager; -import io.api.etherscan.model.Price; -import io.api.etherscan.model.Supply; -import io.api.etherscan.model.utility.PriceResponseTO; -import io.api.etherscan.model.utility.StringResponseTO; -import io.api.etherscan.util.BasicUtils; -import org.jetbrains.annotations.NotNull; - -import java.math.BigInteger; - -/** - * Statistic API Implementation - * - * @see IStatisticApi - * - * @author GoodforGod - * @since 28.10.2018 - */ -public class StatisticApiProvider extends BasicProvider implements IStatisticApi { - - private static final String ACT_SUPPLY_PARAM = ACT_PREFIX + "ethsupply"; - private static final String ACT_TOKEN_SUPPLY_PARAM = ACT_PREFIX + "tokensupply"; - private static final String ACT_LASTPRICE_PARAM = ACT_PREFIX + "ethprice"; - - private static final String CONTRACT_ADDRESS_PARAM = "&contractaddress="; - - StatisticApiProvider(final IQueueManager queue, - final String baseUrl, - final IHttpExecutor executor) { - super(queue, "stats", baseUrl, executor); - } - - @NotNull - @Override - public Supply supply() throws ApiException { - final StringResponseTO response = getRequest(ACT_SUPPLY_PARAM, StringResponseTO.class); - if (response.getStatus() != 1) - throw new EtherScanException(response); - - return new Supply(new BigInteger(response.getResult())); - } - - @NotNull - @Override - public BigInteger supply(final String contract) throws ApiException { - BasicUtils.validateAddress(contract); - - final String urlParams = ACT_TOKEN_SUPPLY_PARAM + CONTRACT_ADDRESS_PARAM + contract; - final StringResponseTO response = getRequest(urlParams, StringResponseTO.class); - if (response.getStatus() != 1) - throw new EtherScanException(response); - - return new BigInteger(response.getResult()); - } - - @NotNull - @Override - public Price lastPrice() throws ApiException { - final PriceResponseTO response = getRequest(ACT_LASTPRICE_PARAM, PriceResponseTO.class); - if (response.getStatus() != 1) - throw new EtherScanException(response); - - return response.getResult(); - } -} diff --git a/src/main/java/io/api/etherscan/error/ApiException.java b/src/main/java/io/api/etherscan/error/ApiException.java deleted file mode 100644 index 33e4228..0000000 --- a/src/main/java/io/api/etherscan/error/ApiException.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.api.etherscan.error; - -/** - * @author GoodforGod - * @since 30.10.2018 - */ -public class ApiException extends RuntimeException { - - public ApiException(String message) { - super(message); - } - - public ApiException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/main/java/io/api/etherscan/error/ApiKeyException.java b/src/main/java/io/api/etherscan/error/ApiKeyException.java deleted file mode 100644 index 4e22934..0000000 --- a/src/main/java/io/api/etherscan/error/ApiKeyException.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.api.etherscan.error; - -/** - * @author GoodforGod - * @since 05.11.2018 - */ -public class ApiKeyException extends ApiException { - - public ApiKeyException(String message) { - super(message); - } -} diff --git a/src/main/java/io/api/etherscan/error/ApiTimeoutException.java b/src/main/java/io/api/etherscan/error/ApiTimeoutException.java deleted file mode 100644 index 39b6e93..0000000 --- a/src/main/java/io/api/etherscan/error/ApiTimeoutException.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.api.etherscan.error; - -/** - * @author GoodforGod - * @since 12.11.2018 - */ -public class ApiTimeoutException extends ApiException { - - public ApiTimeoutException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/main/java/io/api/etherscan/error/ConnectionException.java b/src/main/java/io/api/etherscan/error/ConnectionException.java deleted file mode 100644 index 96a881c..0000000 --- a/src/main/java/io/api/etherscan/error/ConnectionException.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.api.etherscan.error; - -/** - * @author GoodforGod - * @since 29.10.2018 - */ -public class ConnectionException extends ApiException { - - public ConnectionException(String message) { - super(message); - } - - public ConnectionException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/main/java/io/api/etherscan/error/EtherScanException.java b/src/main/java/io/api/etherscan/error/EtherScanException.java deleted file mode 100644 index cb7dd7f..0000000 --- a/src/main/java/io/api/etherscan/error/EtherScanException.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.api.etherscan.error; - -import io.api.etherscan.model.utility.BaseResponseTO; -import io.api.etherscan.model.utility.StringResponseTO; - -/** - * @author GoodforGod - * @since 29.10.2018 - */ -public class EtherScanException extends ApiException { - - public EtherScanException(BaseResponseTO response) { - this(response.getMessage() + ", with status: " + response.getStatus()); - } - - public EtherScanException(StringResponseTO response) { - this(response.getResult() + ", with status: " + response.getStatus() + ", with message: " + response.getMessage()); - } - - public EtherScanException(String message) { - super(message); - } -} diff --git a/src/main/java/io/api/etherscan/error/EventModelException.java b/src/main/java/io/api/etherscan/error/EventModelException.java deleted file mode 100644 index feb60be..0000000 --- a/src/main/java/io/api/etherscan/error/EventModelException.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.api.etherscan.error; - -public class EventModelException extends ApiException { - - public EventModelException(String message) { - super(message); - } - - public EventModelException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/main/java/io/api/etherscan/error/InvalidAddressException.java b/src/main/java/io/api/etherscan/error/InvalidAddressException.java deleted file mode 100644 index 9a0c143..0000000 --- a/src/main/java/io/api/etherscan/error/InvalidAddressException.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.api.etherscan.error; - -/** - * @author GoodforGod - * @since 29.10.2018 - */ -public class InvalidAddressException extends ApiException { - - public InvalidAddressException(String message) { - super(message); - } -} diff --git a/src/main/java/io/api/etherscan/error/InvalidDataHexException.java b/src/main/java/io/api/etherscan/error/InvalidDataHexException.java deleted file mode 100644 index dd12cb9..0000000 --- a/src/main/java/io/api/etherscan/error/InvalidDataHexException.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.api.etherscan.error; - -/** - * @author GoodforGod - * @since 02.11.2018 - */ -public class InvalidDataHexException extends ApiException { - - public InvalidDataHexException(String message) { - super(message); - } -} diff --git a/src/main/java/io/api/etherscan/error/InvalidTxHashException.java b/src/main/java/io/api/etherscan/error/InvalidTxHashException.java deleted file mode 100644 index aba32c1..0000000 --- a/src/main/java/io/api/etherscan/error/InvalidTxHashException.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.api.etherscan.error; - -/** - * @author GoodforGod - * @since 02.11.2018 - */ -public class InvalidTxHashException extends ApiException { - - public InvalidTxHashException(String message) { - super(message); - } -} diff --git a/src/main/java/io/api/etherscan/error/LogQueryException.java b/src/main/java/io/api/etherscan/error/LogQueryException.java deleted file mode 100644 index 504219f..0000000 --- a/src/main/java/io/api/etherscan/error/LogQueryException.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.api.etherscan.error; - -/** - * @author GoodforGod - * @since 31.10.2018 - */ -public class LogQueryException extends ApiException { - - public LogQueryException(String message) { - super(message); - } -} diff --git a/src/main/java/io/api/etherscan/error/RateLimitException.java b/src/main/java/io/api/etherscan/error/RateLimitException.java deleted file mode 100644 index c29f54d..0000000 --- a/src/main/java/io/api/etherscan/error/RateLimitException.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.api.etherscan.error; - -/** - * @author iSnow - * @since 2020-10-06 - */ -public class RateLimitException extends ApiException { - - public RateLimitException(String message) { - super(message); - } -} diff --git a/src/main/java/io/api/etherscan/executor/IHttpExecutor.java b/src/main/java/io/api/etherscan/executor/IHttpExecutor.java deleted file mode 100644 index 0c80282..0000000 --- a/src/main/java/io/api/etherscan/executor/IHttpExecutor.java +++ /dev/null @@ -1,27 +0,0 @@ -package io.api.etherscan.executor; - -/** - * Http Client interface - * - * @author GoodforGod - * @since 31.10.2018 - */ -public interface IHttpExecutor { - - /** - * Performs a Http GET request - * - * @param url as string - * @return result as string - */ - String get(String url); - - /** - * Performs a Http POST request - * - * @param url as string - * @param data to post - * @return result as string - */ - String post(String url, String data); -} diff --git a/src/main/java/io/api/etherscan/executor/impl/HttpExecutor.java b/src/main/java/io/api/etherscan/executor/impl/HttpExecutor.java deleted file mode 100644 index 5ba39f2..0000000 --- a/src/main/java/io/api/etherscan/executor/impl/HttpExecutor.java +++ /dev/null @@ -1,161 +0,0 @@ -package io.api.etherscan.executor.impl; - -import io.api.etherscan.error.ApiTimeoutException; -import io.api.etherscan.error.ConnectionException; -import io.api.etherscan.executor.IHttpExecutor; -import io.api.etherscan.util.BasicUtils; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.HttpURLConnection; -import java.net.SocketTimeoutException; -import java.net.URL; -import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; -import java.util.zip.GZIPInputStream; -import java.util.zip.InflaterInputStream; - -import static java.net.HttpURLConnection.*; - -/** - * Http client implementation - * - * @author GoodforGod - * @see IHttpExecutor - * @since 28.10.2018 - */ -public class HttpExecutor implements IHttpExecutor { - - private static final Map DEFAULT_HEADERS = new HashMap<>(); - - private static final int CONNECT_TIMEOUT = 8000; - private static final int READ_TIMEOUT = 0; - - static { - DEFAULT_HEADERS.put("Accept-Language", "en"); - DEFAULT_HEADERS.put("Accept-Encoding", "deflate, gzip"); - DEFAULT_HEADERS.put("User-Agent", "Chrome/68.0.3440.106"); - DEFAULT_HEADERS.put("Accept-Charset", "UTF-8"); - } - - private final Map headers; - private final int connectTimeout; - private final int readTimeout; - - public HttpExecutor() { - this(CONNECT_TIMEOUT); - } - - public HttpExecutor(final int connectTimeout) { - this(connectTimeout, READ_TIMEOUT); - } - - public HttpExecutor(final int connectTimeout, final int readTimeout) { - this(connectTimeout, readTimeout, DEFAULT_HEADERS); - } - - /** - * @param connectTimeout custom connection establish timeout in millis - * @param readTimeout custom read timeout in millis - * @param headers custom HTTP headers - */ - public HttpExecutor(final int connectTimeout, - final int readTimeout, - final Map headers) { - this.connectTimeout = Math.max(connectTimeout, 0); - this.readTimeout = Math.max(readTimeout, 0); - this.headers = headers; - } - - private HttpURLConnection buildConnection(String urlAsString, String method) throws IOException { - final URL url = new URL(urlAsString); - final HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - connection.setRequestMethod(method); - connection.setConnectTimeout(connectTimeout); - connection.setReadTimeout(readTimeout); - headers.forEach(connection::setRequestProperty); - return connection; - } - - @Override - public String get(final String urlAsString) { - try { - final HttpURLConnection connection = buildConnection(urlAsString, "GET"); - final int status = connection.getResponseCode(); - if (status == HTTP_MOVED_TEMP || status == HTTP_MOVED_PERM) { - return get(connection.getHeaderField("Location")); - } else if ((status >= HTTP_BAD_REQUEST) && (status < HTTP_INTERNAL_ERROR)) { - throw new ConnectionException("Protocol error: " + connection.getResponseMessage()); - } else if (status >= HTTP_INTERNAL_ERROR) { - throw new ConnectionException("Server error: " + connection.getResponseMessage()); - } - - final String data = readData(connection); - connection.disconnect(); - return data; - } catch (SocketTimeoutException e) { - throw new ApiTimeoutException("Timeout: Could not establish connection for " + connectTimeout + " millis", e); - } catch (Exception e) { - throw new ConnectionException(e.getMessage(), e); - } - } - - @Override - public String post(final String urlAsString, final String dataToPost) { - try { - final HttpURLConnection connection = buildConnection(urlAsString, "POST"); - final String contentLength = (BasicUtils.isBlank(dataToPost)) ? "0" : String.valueOf(dataToPost.length()); - connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); - connection.setRequestProperty("Content-Length", contentLength); - connection.setFixedLengthStreamingMode(dataToPost.length()); - - connection.setDoOutput(true); - connection.connect(); - try (OutputStream os = connection.getOutputStream()) { - os.write(dataToPost.getBytes(StandardCharsets.UTF_8)); - } - - final int status = connection.getResponseCode(); - if (status == HTTP_MOVED_TEMP || status == HTTP_MOVED_PERM) { - return post(connection.getHeaderField("Location"), dataToPost); - } else if ((status >= HTTP_BAD_REQUEST) && (status < HTTP_INTERNAL_ERROR)) { - throw new ConnectionException("Protocol error: " + connection.getResponseMessage()); - } else if (status >= HTTP_INTERNAL_ERROR) { - throw new ConnectionException("Server error: " + connection.getResponseMessage()); - } - - final String data = readData(connection); - connection.disconnect(); - return data; - } catch (SocketTimeoutException e) { - throw new ApiTimeoutException("Timeout: Could not establish connection for " + connectTimeout + " millis", e); - } catch (Exception e) { - throw new ConnectionException(e.getMessage(), e); - } - } - - private String readData(final HttpURLConnection connection) throws IOException { - final StringBuilder content = new StringBuilder(); - try (BufferedReader in = new BufferedReader(getStreamReader(connection))) { - String inputLine; - while ((inputLine = in.readLine()) != null) - content.append(inputLine); - } - - return content.toString(); - } - - private InputStreamReader getStreamReader(final HttpURLConnection connection) throws IOException { - switch (String.valueOf(connection.getContentEncoding())) { - case "gzip": - return new InputStreamReader(new GZIPInputStream(connection.getInputStream()), StandardCharsets.UTF_8); - case "deflate": - return new InputStreamReader(new InflaterInputStream(connection.getInputStream()), StandardCharsets.UTF_8); - default: - return new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8); - } - } -} diff --git a/src/main/java/io/api/etherscan/manager/IQueueManager.java b/src/main/java/io/api/etherscan/manager/IQueueManager.java deleted file mode 100644 index 98a3172..0000000 --- a/src/main/java/io/api/etherscan/manager/IQueueManager.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.api.etherscan.manager; - -/** - * Queue manager to support API limits (EtherScan 5request\sec limit) Managers grants turn if the - * limit is not exhausted And resets queue each set period - * - * @author GoodforGod - * @since 30.10.2018 - */ -public interface IQueueManager extends AutoCloseable { - - /** - * Waits in queue for chance to take turn - */ - void takeTurn(); -} diff --git a/src/main/java/io/api/etherscan/manager/impl/QueueManager.java b/src/main/java/io/api/etherscan/manager/impl/QueueManager.java deleted file mode 100644 index 764f7d5..0000000 --- a/src/main/java/io/api/etherscan/manager/impl/QueueManager.java +++ /dev/null @@ -1,64 +0,0 @@ -package io.api.etherscan.manager.impl; - -import io.api.etherscan.manager.IQueueManager; - -import java.util.concurrent.*; - -/** - * Queue Semaphore implementation with size and reset time as params - * - * @see IQueueManager - * - * @author GoodforGod - * @since 30.10.2018 - */ -public class QueueManager implements IQueueManager, AutoCloseable { - - public static final QueueManager DEFAULT_KEY_QUEUE = new QueueManager(1, 5200L, 5200L, 0); - public static final QueueManager PERSONAL_KEY_QUEUE = new QueueManager(5, 1100L, 1100L, 5); - - private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); - private final Semaphore semaphore; - private final long queueResetTimeInMillis; - - public QueueManager(int size, int resetInSec) { - this(size, resetInSec, resetInSec); - } - - public QueueManager(int size, int queueResetTimeInSec, int delayInSec) { - this(size, queueResetTimeInSec, delayInSec, size); - } - - public QueueManager(int size, int queueResetTimeInSec, int delayInSec, int initialSize) { - this(size, - (long) queueResetTimeInSec * 1000, - (long) delayInSec * 1000, - initialSize); - } - - public QueueManager(int size, long queueResetTimeInMillis, long delayInMillis, int initialSize) { - this.queueResetTimeInMillis = queueResetTimeInMillis; - this.semaphore = new Semaphore(initialSize); - this.executorService.scheduleAtFixedRate(releaseLocks(size), delayInMillis, queueResetTimeInMillis, - TimeUnit.MILLISECONDS); - } - - @SuppressWarnings("java:S899") - @Override - public void takeTurn() { - try { - semaphore.tryAcquire(queueResetTimeInMillis, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - - private Runnable releaseLocks(int toRelease) { - return () -> semaphore.release(toRelease); - } - - @Override - public void close() { - executorService.shutdown(); - } -} diff --git a/src/main/java/io/api/etherscan/model/Balance.java b/src/main/java/io/api/etherscan/model/Balance.java deleted file mode 100644 index cbd8502..0000000 --- a/src/main/java/io/api/etherscan/model/Balance.java +++ /dev/null @@ -1,84 +0,0 @@ -package io.api.etherscan.model; - -import io.api.etherscan.model.utility.BalanceTO; - -import java.math.BigInteger; -import java.util.Objects; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 28.10.2018 - */ -public class Balance { - - /** Balance in Wei */ - private final Wei balance; - private final String address; - - public Balance(final String address, - final BigInteger balance) { - this.address = address; - this.balance = new Wei(balance); - } - - public static Balance of(BalanceTO balance) { - return new Balance(balance.getAccount(), new BigInteger(balance.getBalance())); - } - - // - public String getAddress() { - return address; - } - - public BigInteger getWei() { - return balance.getValue(); - } - - public BigInteger getKwei() { - return balance.asKwei(); - } - - public BigInteger getMwei() { - return balance.asMwei(); - } - - public BigInteger getGwei() { - return balance.asGwei(); - } - - public BigInteger getEther() { - return balance.asEther(); - } - // - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - - Balance balance1 = (Balance) o; - - if (!balance.equals(balance1.balance)) - return false; - return Objects.equals(address, balance1.address); - } - - @Override - public int hashCode() { - int result = balance.hashCode(); - result = 31 * result + (address != null ? address.hashCode() : 0); - return result; - } - - @Override - public String toString() { - return "Balance{" + - "address='" + address + '\'' + - ", balance=" + balance + - '}'; - } -} diff --git a/src/main/java/io/api/etherscan/model/BaseTx.java b/src/main/java/io/api/etherscan/model/BaseTx.java deleted file mode 100644 index 6eba826..0000000 --- a/src/main/java/io/api/etherscan/model/BaseTx.java +++ /dev/null @@ -1,124 +0,0 @@ -package io.api.etherscan.model; - -import com.google.gson.annotations.Expose; -import io.api.etherscan.util.BasicUtils; - -import java.math.BigInteger; -import java.time.LocalDateTime; -import java.time.ZoneOffset; -import java.util.Objects; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 28.10.2018 - */ -abstract class BaseTx { - - private long blockNumber; - private String timeStamp; - @Expose(serialize = false, deserialize = false) - private LocalDateTime _timeStamp; - private String hash; - private String from; - private String to; - private BigInteger value; - private String contractAddress; - private String input; - private BigInteger gas; - private BigInteger gasUsed; - - // - public long getBlockNumber() { - return blockNumber; - } - - public LocalDateTime getTimeStamp() { - if (_timeStamp == null && !BasicUtils.isEmpty(timeStamp)) - _timeStamp = LocalDateTime.ofEpochSecond(Long.parseLong(timeStamp), 0, ZoneOffset.UTC); - return _timeStamp; - } - - public String getHash() { - return hash; - } - - public String getFrom() { - return from; - } - - public String getTo() { - return to; - } - - public BigInteger getValue() { - return value; - } - - public String getContractAddress() { - return contractAddress; - } - - public String getInput() { - return input; - } - - public BigInteger getGas() { - return gas; - } - - public BigInteger getGasUsed() { - return gasUsed; - } - // - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (!(o instanceof BaseTx)) - return false; - - BaseTx baseTx = (BaseTx) o; - - if (blockNumber != baseTx.blockNumber) - return false; - if (!Objects.equals(timeStamp, baseTx.timeStamp)) - return false; - if (!Objects.equals(hash, baseTx.hash)) - return false; - if (!Objects.equals(from, baseTx.from)) - return false; - if (!Objects.equals(to, baseTx.to)) - return false; - return Objects.equals(value, baseTx.value); - } - - @Override - public int hashCode() { - int result = (int) (blockNumber ^ (blockNumber >>> 32)); - result = 31 * result + (timeStamp != null ? timeStamp.hashCode() : 0); - result = 31 * result + (hash != null ? hash.hashCode() : 0); - result = 31 * result + (from != null ? from.hashCode() : 0); - result = 31 * result + (to != null ? to.hashCode() : 0); - result = 31 * result + (value != null ? value.hashCode() : 0); - return result; - } - - @Override - public String toString() { - return "BaseTx{" + - "blockNumber=" + blockNumber + - ", timeStamp='" + timeStamp + '\'' + - ", hash='" + hash + '\'' + - ", from='" + from + '\'' + - ", to='" + to + '\'' + - ", value=" + value + - ", contractAddress='" + contractAddress + '\'' + - ", input='" + input + '\'' + - ", gas=" + gas + - ", gasUsed=" + gasUsed + - '}'; - } -} diff --git a/src/main/java/io/api/etherscan/model/Block.java b/src/main/java/io/api/etherscan/model/Block.java deleted file mode 100644 index 8853956..0000000 --- a/src/main/java/io/api/etherscan/model/Block.java +++ /dev/null @@ -1,66 +0,0 @@ -package io.api.etherscan.model; - -import com.google.gson.annotations.Expose; -import io.api.etherscan.util.BasicUtils; - -import java.math.BigInteger; -import java.time.LocalDateTime; -import java.time.ZoneOffset; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 28.10.2018 - */ -public class Block { - - private long blockNumber; - private BigInteger blockReward; - private String timeStamp; - @Expose(serialize = false, deserialize = false) - private LocalDateTime _timeStamp; - - // - public long getBlockNumber() { - return blockNumber; - } - - public LocalDateTime getTimeStamp() { - if (_timeStamp == null && !BasicUtils.isEmpty(timeStamp)) - _timeStamp = LocalDateTime.ofEpochSecond(Long.parseLong(timeStamp), 0, ZoneOffset.UTC); - return _timeStamp; - } - - public BigInteger getBlockReward() { - return blockReward; - } - // - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - - Block block = (Block) o; - - return blockNumber == block.blockNumber; - } - - @Override - public int hashCode() { - return (int) (blockNumber ^ (blockNumber >>> 32)); - } - - @Override - public String toString() { - return "Block{" + - "blockNumber=" + blockNumber + - ", blockReward=" + blockReward + - ", timeStamp='" + timeStamp + '\'' + - ", _timeStamp=" + _timeStamp + - '}'; - } -} diff --git a/src/main/java/io/api/etherscan/model/EthNetwork.java b/src/main/java/io/api/etherscan/model/EthNetwork.java deleted file mode 100644 index f7b91de..0000000 --- a/src/main/java/io/api/etherscan/model/EthNetwork.java +++ /dev/null @@ -1,27 +0,0 @@ -package io.api.etherscan.model; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 28.10.2018 - */ -public enum EthNetwork { - - MAINNET("api"), - ROPSTEN("api-ropsten"), - KOVAN("api-kovan"), - TOBALABA("api-tobalaba"), - GORLI("api-goerli"), - RINKEBY("api-rinkeby"); - - private final String domain; - - EthNetwork(String domain) { - this.domain = domain; - } - - public String getDomain() { - return domain; - } -} diff --git a/src/main/java/io/api/etherscan/model/Log.java b/src/main/java/io/api/etherscan/model/Log.java deleted file mode 100644 index 67ce96f..0000000 --- a/src/main/java/io/api/etherscan/model/Log.java +++ /dev/null @@ -1,176 +0,0 @@ -package io.api.etherscan.model; - -import com.google.gson.annotations.Expose; -import io.api.etherscan.util.BasicUtils; - -import java.math.BigInteger; -import java.time.LocalDateTime; -import java.time.ZoneOffset; -import java.util.List; -import java.util.Objects; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 31.10.2018 - */ -public class Log { - - private String blockNumber; - @Expose(serialize = false, deserialize = false) - private Long _blockNumber; - private String address; - private String transactionHash; - private String transactionIndex; - @Expose(serialize = false, deserialize = false) - private Long _transactionIndex; - private String timeStamp; - @Expose(serialize = false, deserialize = false) - private LocalDateTime _timeStamp; - private String data; - private String gasPrice; - @Expose(serialize = false, deserialize = false) - private BigInteger _gasPrice; - private String gasUsed; - @Expose(serialize = false, deserialize = false) - private BigInteger _gasUsed; - private List topics; - private String logIndex; - @Expose(serialize = false, deserialize = false) - private Long _logIndex; - - // - public Long getBlockNumber() { - if (_blockNumber == null && !BasicUtils.isEmpty(blockNumber)) { - _blockNumber = BasicUtils.parseHex(blockNumber).longValue(); - } - return _blockNumber; - } - - public String getAddress() { - return address; - } - - public String getTransactionHash() { - return transactionHash; - } - - public Long getTransactionIndex() { - if (_transactionIndex == null && !BasicUtils.isEmpty(transactionIndex)) { - _transactionIndex = BasicUtils.parseHex(transactionIndex).longValue(); - } - - return _transactionIndex; - } - - public LocalDateTime getTimeStamp() { - if (_timeStamp == null && !BasicUtils.isEmpty(timeStamp)) { - long formatted = (timeStamp.charAt(0) == '0' && timeStamp.charAt(1) == 'x') - ? BasicUtils.parseHex(timeStamp).longValue() - : Long.parseLong(timeStamp); - _timeStamp = LocalDateTime.ofEpochSecond(formatted, 0, ZoneOffset.UTC); - } - return _timeStamp; - } - - /** - * Return the "timeStamp" field of the event record as a long-int representing the milliseconds - * since the Unix epoch (1970-01-01 00:00:00). - * - * @return milliseconds between Unix epoch and `timeStamp`. If field is empty or null, returns null - */ - public Long getTimeStampAsMillis() { - if (BasicUtils.isEmpty(timeStamp)) { - return null; - } - long tsSecs = (timeStamp.charAt(0) == '0' && timeStamp.charAt(1) == 'x') - ? BasicUtils.parseHex(timeStamp).longValue() - : Long.parseLong(timeStamp); - return tsSecs * 1000; - } - - public String getData() { - return data; - } - - public BigInteger getGasPrice() { - if (!BasicUtils.isEmpty(gasPrice)) { - _gasPrice = BasicUtils.parseHex(gasPrice); - } - - return _gasPrice; - } - - public BigInteger getGasUsed() { - if (!BasicUtils.isEmpty(gasUsed)) { - _gasUsed = BasicUtils.parseHex(gasUsed); - } - - return _gasUsed; - } - - public List getTopics() { - return topics; - } - - public Long getLogIndex() { - if (_logIndex == null && !BasicUtils.isEmpty(logIndex)) { - _logIndex = BasicUtils.parseHex(logIndex).longValue(); - } - return _logIndex; - } - // - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - - Log log = (Log) o; - - if (!Objects.equals(blockNumber, log.blockNumber)) - return false; - if (!Objects.equals(address, log.address)) - return false; - if (!Objects.equals(transactionHash, log.transactionHash)) - return false; - if (!Objects.equals(timeStamp, log.timeStamp)) - return false; - return Objects.equals(logIndex, log.logIndex); - } - - @Override - public int hashCode() { - int result = blockNumber != null ? blockNumber.hashCode() : 0; - result = 31 * result + (address != null ? address.hashCode() : 0); - result = 31 * result + (transactionHash != null ? transactionHash.hashCode() : 0); - result = 31 * result + (timeStamp != null ? timeStamp.hashCode() : 0); - result = 31 * result + (logIndex != null ? logIndex.hashCode() : 0); - return result; - } - - @Override - public String toString() { - return "Log{" + - "blockNumber='" + blockNumber + '\'' + - ", _blockNumber=" + _blockNumber + - ", address='" + address + '\'' + - ", transactionHash='" + transactionHash + '\'' + - ", transactionIndex='" + transactionIndex + '\'' + - ", _transactionIndex=" + _transactionIndex + - ", timeStamp='" + timeStamp + '\'' + - ", _timeStamp=" + _timeStamp + - ", data='" + data + '\'' + - ", gasPrice='" + gasPrice + '\'' + - ", _gasPrice=" + _gasPrice + - ", gasUsed='" + gasUsed + '\'' + - ", _gasUsed=" + _gasUsed + - ", topics=" + topics + - ", logIndex='" + logIndex + '\'' + - ", _logIndex=" + _logIndex + - '}'; - } -} diff --git a/src/main/java/io/api/etherscan/model/Price.java b/src/main/java/io/api/etherscan/model/Price.java deleted file mode 100644 index 9bc7dc7..0000000 --- a/src/main/java/io/api/etherscan/model/Price.java +++ /dev/null @@ -1,85 +0,0 @@ -package io.api.etherscan.model; - -import com.google.gson.annotations.Expose; - -import java.time.LocalDateTime; -import java.time.ZoneOffset; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 30.10.2018 - */ -public class Price { - - private double ethusd; - private double ethbtc; - private String ethusd_timestamp; - private String ethbtc_timestamp; - @Expose(serialize = false, deserialize = false) - private LocalDateTime _ethusd_timestamp; - @Expose(serialize = false, deserialize = false) - private LocalDateTime _ethbtc_timestamp; - - public double inUsd() { - return ethusd; - } - - public double inBtc() { - return ethbtc; - } - - public LocalDateTime usdTimestamp() { - if (_ethusd_timestamp == null) - _ethusd_timestamp = LocalDateTime.ofEpochSecond(Long.parseLong(ethusd_timestamp), 0, ZoneOffset.UTC); - return _ethusd_timestamp; - } - - public LocalDateTime btcTimestamp() { - if (_ethbtc_timestamp == null) - _ethbtc_timestamp = LocalDateTime.ofEpochSecond(Long.parseLong(ethbtc_timestamp), 0, ZoneOffset.UTC); - return _ethbtc_timestamp; - } - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - - Price price = (Price) o; - - if (Double.compare(price.ethusd, ethusd) != 0) - return false; - if (Double.compare(price.ethbtc, ethbtc) != 0) - return false; - if (ethusd_timestamp != null ? !ethusd_timestamp.equals(price.ethusd_timestamp) : price.ethusd_timestamp != null) - return false; - return (ethbtc_timestamp != null ? !ethbtc_timestamp.equals(price.ethbtc_timestamp) : price.ethbtc_timestamp != null); - } - - @Override - public int hashCode() { - int result; - long temp; - temp = Double.doubleToLongBits(ethusd); - result = (int) (temp ^ (temp >>> 32)); - temp = Double.doubleToLongBits(ethbtc); - result = 31 * result + (int) (temp ^ (temp >>> 32)); - result = 31 * result + (ethusd_timestamp != null ? ethusd_timestamp.hashCode() : 0); - result = 31 * result + (ethbtc_timestamp != null ? ethbtc_timestamp.hashCode() : 0); - return result; - } - - @Override - public String toString() { - return "Price{" + - "ethusd=" + ethusd + - ", ethbtc=" + ethbtc + - ", ethusd_timestamp='" + ethusd_timestamp + '\'' + - ", ethbtc_timestamp='" + ethbtc_timestamp + '\'' + - '}'; - } -} diff --git a/src/main/java/io/api/etherscan/model/Status.java b/src/main/java/io/api/etherscan/model/Status.java deleted file mode 100644 index 9683bde..0000000 --- a/src/main/java/io/api/etherscan/model/Status.java +++ /dev/null @@ -1,55 +0,0 @@ -package io.api.etherscan.model; - -import java.util.Objects; - -/** - * Contract Execution Status - * - * @author GoodforGod - * @since 30.10.2018 - */ -public class Status { - - /** - * "0" = Pass , isError":"1" = Error during Contract Execution - */ - private int isError; - private String errDescription; - - public boolean haveError() { - return isError == 1; - } - - public String getErrDescription() { - return errDescription; - } - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - - Status status = (Status) o; - - if (isError != status.isError) - return false; - return Objects.equals(errDescription, status.errDescription); - } - - @Override - public int hashCode() { - int result = isError; - result = 31 * result + (errDescription != null ? errDescription.hashCode() : 0); - return result; - } - - @Override - public String toString() { - return "Status{" + - "isError=" + isError + - ", errDescription='" + errDescription + '\'' + - '}'; - } -} diff --git a/src/main/java/io/api/etherscan/model/Supply.java b/src/main/java/io/api/etherscan/model/Supply.java deleted file mode 100644 index 2fd6db7..0000000 --- a/src/main/java/io/api/etherscan/model/Supply.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.api.etherscan.model; - -import java.math.BigInteger; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 30.10.2018 - */ -public class Supply extends Wei { - - public Supply(BigInteger value) { - super(value); - } -} diff --git a/src/main/java/io/api/etherscan/model/Tx.java b/src/main/java/io/api/etherscan/model/Tx.java deleted file mode 100644 index 4136d23..0000000 --- a/src/main/java/io/api/etherscan/model/Tx.java +++ /dev/null @@ -1,102 +0,0 @@ -package io.api.etherscan.model; - -import io.api.etherscan.util.BasicUtils; - -import java.math.BigInteger; -import java.util.Objects; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 28.10.2018 - */ -public class Tx extends BaseTx { - - private long nonce; - private String blockHash; - private int transactionIndex; - private BigInteger gasPrice; - private BigInteger cumulativeGasUsed; - private long confirmations; - private String isError; - private String txreceipt_status; - - // - public long getNonce() { - return nonce; - } - - public String getBlockHash() { - return blockHash; - } - - public int getTransactionIndex() { - return transactionIndex; - } - - public BigInteger getGasPrice() { - return gasPrice; - } - - public boolean haveError() { - return !BasicUtils.isEmpty(isError) && !isError.equals("0"); - } - - public String getTxreceipt_status() { - return txreceipt_status; - } - - public BigInteger getCumulativeGasUsed() { - return cumulativeGasUsed; - } - - public long getConfirmations() { - return confirmations; - } - // - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - if (!super.equals(o)) - return false; - - Tx tx = (Tx) o; - - if (nonce != tx.nonce) - return false; - if (transactionIndex != tx.transactionIndex) - return false; - if (!Objects.equals(blockHash, tx.blockHash)) - return false; - return Objects.equals(isError, tx.isError); - } - - @Override - public int hashCode() { - int result = super.hashCode(); - result = 31 * result + (int) (nonce ^ (nonce >>> 32)); - result = 31 * result + (blockHash != null ? blockHash.hashCode() : 0); - result = 31 * result + transactionIndex; - result = 31 * result + (isError != null ? isError.hashCode() : 0); - return result; - } - - @Override - public String toString() { - return "Tx{" + - "nonce=" + nonce + - ", blockHash='" + blockHash + '\'' + - ", transactionIndex=" + transactionIndex + - ", gasPrice=" + gasPrice + - ", cumulativeGasUsed=" + cumulativeGasUsed + - ", confirmations=" + confirmations + - ", isError='" + isError + '\'' + - ", txreceipt_status='" + txreceipt_status + '\'' + - "} " + super.toString(); - } -} diff --git a/src/main/java/io/api/etherscan/model/TxInternal.java b/src/main/java/io/api/etherscan/model/TxInternal.java deleted file mode 100644 index 5048947..0000000 --- a/src/main/java/io/api/etherscan/model/TxInternal.java +++ /dev/null @@ -1,73 +0,0 @@ -package io.api.etherscan.model; - -import java.util.Objects; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 29.10.2018 - */ -public class TxInternal extends BaseTx { - - private String type; - private String traceId; - private int isError; - private String errCode; - - // - public String getType() { - return type; - } - - public long getTraceId() { - return (traceId == null) ? 0 : Long.parseLong(traceId); - } - - public String getTraceIdAsString() { - return traceId; - } - - public boolean haveError() { - return isError == 1; - } - - public String getErrCode() { - return errCode; - } - // - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (!(o instanceof TxInternal)) - return false; - if (!super.equals(o)) - return false; - - TxInternal that = (TxInternal) o; - - if (!Objects.equals(traceId, that.traceId)) - return false; - return Objects.equals(errCode, that.errCode); - } - - @Override - public int hashCode() { - int result = super.hashCode(); - result = 31 * result + (traceId != null ? traceId.hashCode() : 0); - result = 31 * result + (errCode != null ? errCode.hashCode() : 0); - return result; - } - - @Override - public String toString() { - return "TxInternal{" + - "type='" + type + '\'' + - ", traceId=" + traceId + - ", isError=" + isError + - ", errCode='" + errCode + '\'' + - "} " + super.toString(); - } -} diff --git a/src/main/java/io/api/etherscan/model/TxToken.java b/src/main/java/io/api/etherscan/model/TxToken.java deleted file mode 100644 index 8f5e36f..0000000 --- a/src/main/java/io/api/etherscan/model/TxToken.java +++ /dev/null @@ -1,73 +0,0 @@ -package io.api.etherscan.model; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 28.10.2018 - */ -public class TxToken extends BaseTx { - - private long nonce; - private String blockHash; - private String tokenName; - private String tokenSymbol; - private String tokenDecimal; - private int transactionIndex; - private long gasPrice; - private long cumulativeGasUsed; - private long confirmations; - - // - public long getNonce() { - return nonce; - } - - public String getBlockHash() { - return blockHash; - } - - public String getTokenName() { - return tokenName; - } - - public String getTokenSymbol() { - return tokenSymbol; - } - - public String getTokenDecimal() { - return tokenDecimal; - } - - public int getTransactionIndex() { - return transactionIndex; - } - - public long getGasPrice() { - return gasPrice; - } - - public long getCumulativeGasUsed() { - return cumulativeGasUsed; - } - - public long getConfirmations() { - return confirmations; - } - // - - @Override - public String toString() { - return "TxToken{" + - "nonce=" + nonce + - ", blockHash='" + blockHash + '\'' + - ", tokenName='" + tokenName + '\'' + - ", tokenSymbol='" + tokenSymbol + '\'' + - ", tokenDecimal='" + tokenDecimal + '\'' + - ", transactionIndex=" + transactionIndex + - ", gasPrice=" + gasPrice + - ", cumulativeGasUsed=" + cumulativeGasUsed + - ", confirmations=" + confirmations + - "} " + super.toString(); - } -} diff --git a/src/main/java/io/api/etherscan/model/Uncle.java b/src/main/java/io/api/etherscan/model/Uncle.java deleted file mode 100644 index 2ee206b..0000000 --- a/src/main/java/io/api/etherscan/model/Uncle.java +++ /dev/null @@ -1,63 +0,0 @@ -package io.api.etherscan.model; - -import java.math.BigInteger; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 30.10.2018 - */ -public class Uncle { - - private String miner; - private BigInteger blockreward; - private int unclePosition; - - // - public String getMiner() { - return miner; - } - - public BigInteger getBlockreward() { - return blockreward; - } - - public int getUnclePosition() { - return unclePosition; - } - // - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - - Uncle uncle = (Uncle) o; - - if (unclePosition != uncle.unclePosition) - return false; - if (miner != null ? !miner.equals(uncle.miner) : uncle.miner != null) - return false; - return blockreward != null ? blockreward.equals(uncle.blockreward) : uncle.blockreward == null; - } - - @Override - public int hashCode() { - int result = miner != null ? miner.hashCode() : 0; - result = 31 * result + (blockreward != null ? blockreward.hashCode() : 0); - result = 31 * result + unclePosition; - return result; - } - - @Override - public String toString() { - return "Uncle{" + - "miner='" + miner + '\'' + - ", blockreward=" + blockreward + - ", unclePosition=" + unclePosition + - '}'; - } -} diff --git a/src/main/java/io/api/etherscan/model/UncleBlock.java b/src/main/java/io/api/etherscan/model/UncleBlock.java deleted file mode 100644 index 88c975d..0000000 --- a/src/main/java/io/api/etherscan/model/UncleBlock.java +++ /dev/null @@ -1,68 +0,0 @@ -package io.api.etherscan.model; - -import io.api.etherscan.util.BasicUtils; - -import java.util.List; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 30.10.2018 - */ -public class UncleBlock extends Block { - - private String blockMiner; - private List uncles; - private String uncleInclusionReward; - - // - public boolean isEmpty() { - return getBlockNumber() == 0 && getBlockReward() == null - && getTimeStamp() == null - && BasicUtils.isEmpty(blockMiner); - } - - public String getBlockMiner() { - return blockMiner; - } - - public List getUncles() { - return uncles; - } - - public String getUncleInclusionReward() { - return uncleInclusionReward; - } - // - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - if (!super.equals(o)) - return false; - - UncleBlock that = (UncleBlock) o; - - return getBlockNumber() != 0 && getBlockNumber() == that.getBlockNumber(); - } - - @Override - public int hashCode() { - int result = super.hashCode(); - result = (int) (31 * result + getBlockNumber()); - return result; - } - - @Override - public String toString() { - return "UncleBlock{" + - "blockMiner='" + blockMiner + '\'' + - ", uncles=" + uncles + - ", uncleInclusionReward='" + uncleInclusionReward + '\'' + - '}'; - } -} diff --git a/src/main/java/io/api/etherscan/model/Wei.java b/src/main/java/io/api/etherscan/model/Wei.java deleted file mode 100644 index eddf8d2..0000000 --- a/src/main/java/io/api/etherscan/model/Wei.java +++ /dev/null @@ -1,64 +0,0 @@ -package io.api.etherscan.model; - -import java.math.BigInteger; -import java.util.Objects; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 30.10.2018 - */ -public class Wei { - - private BigInteger result; - - public Wei(BigInteger value) { - this.result = value; - } - - // - public BigInteger getValue() { - return result; - } - - public BigInteger asKwei() { - return result.divide(BigInteger.valueOf(1000)); - } - - public BigInteger asMwei() { - return result.divide(BigInteger.valueOf(1000000)); - } - - public BigInteger asGwei() { - return result.divide(BigInteger.valueOf(1000000000)); - } - - public BigInteger asEther() { - return result.divide(BigInteger.valueOf(1000000000000000L)); - } - // - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - - Wei wei = (Wei) o; - return Objects.equals(result, wei.result); - } - - @Override - public int hashCode() { - return result != null ? result.hashCode() : 0; - } - - @Override - public String toString() { - return "Wei{" + - "result=" + result + - '}'; - } -} diff --git a/src/main/java/io/api/etherscan/model/proxy/BlockProxy.java b/src/main/java/io/api/etherscan/model/proxy/BlockProxy.java deleted file mode 100644 index 63821c0..0000000 --- a/src/main/java/io/api/etherscan/model/proxy/BlockProxy.java +++ /dev/null @@ -1,199 +0,0 @@ -package io.api.etherscan.model.proxy; - -import com.google.gson.annotations.Expose; -import io.api.etherscan.util.BasicUtils; - -import java.math.BigInteger; -import java.time.LocalDateTime; -import java.time.ZoneOffset; -import java.util.List; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 31.10.2018 - */ -public class BlockProxy { - - private String number; - @Expose(serialize = false, deserialize = false) - private Long _number; - private String hash; - private String parentHash; - private String stateRoot; - private String size; - @Expose(serialize = false, deserialize = false) - private Long _size; - private String difficulty; - private String totalDifficulty; - private String timestamp; - @Expose(serialize = false, deserialize = false) - private LocalDateTime _timestamp; - - private String miner; - private String nonce; - private String extraData; - private String logsBloom; - private String mixHash; - private String gasUsed; - @Expose(serialize = false, deserialize = false) - private BigInteger _gasUsed; - private String gasLimit; - @Expose(serialize = false, deserialize = false) - private BigInteger _gasLimit; - - private String sha3Uncles; - private List uncles; - - private String receiptsRoot; - private String transactionsRoot; - private List transactions; - - // - public Long getNumber() { - if (_number == null && !BasicUtils.isEmpty(number)) - _number = BasicUtils.parseHex(number).longValue(); - return _number; - } - - public String getHash() { - return hash; - } - - public String getParentHash() { - return parentHash; - } - - public String getStateRoot() { - return stateRoot; - } - - public Long getSize() { - if (_size == null && !BasicUtils.isEmpty(size)) - _size = BasicUtils.parseHex(size).longValue(); - return _size; - } - - public String getDifficulty() { - return difficulty; - } - - public String getTotalDifficulty() { - return totalDifficulty; - } - - public LocalDateTime getTimeStamp() { - if (_timestamp == null && !BasicUtils.isEmpty(timestamp)) - _timestamp = LocalDateTime.ofEpochSecond(BasicUtils.parseHex(timestamp).longValue(), 0, ZoneOffset.UTC); - return _timestamp; - } - - public String getMiner() { - return miner; - } - - public String getNonce() { - return nonce; - } - - public String getExtraData() { - return extraData; - } - - public String getLogsBloom() { - return logsBloom; - } - - public String getMixHash() { - return mixHash; - } - - public BigInteger getGasUsed() { - if (_gasUsed == null && !BasicUtils.isEmpty(gasUsed)) - _gasUsed = BasicUtils.parseHex(gasUsed); - return _gasUsed; - } - - public BigInteger getGasLimit() { - if (_gasLimit == null && !BasicUtils.isEmpty(gasLimit)) - _gasLimit = BasicUtils.parseHex(gasLimit); - return _gasLimit; - } - - public String getSha3Uncles() { - return sha3Uncles; - } - - public List getUncles() { - return uncles; - } - - public String getReceiptsRoot() { - return receiptsRoot; - } - - public String getTransactionsRoot() { - return transactionsRoot; - } - - public List getTransactions() { - return transactions; - } - // - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - - BlockProxy that = (BlockProxy) o; - - if (number != null ? !number.equals(that.number) : that.number != null) - return false; - if (hash != null ? !hash.equals(that.hash) : that.hash != null) - return false; - return parentHash != null ? parentHash.equals(that.parentHash) : that.parentHash == null; - } - - @Override - public int hashCode() { - int result = number != null ? number.hashCode() : 0; - result = 31 * result + (hash != null ? hash.hashCode() : 0); - result = 31 * result + (parentHash != null ? parentHash.hashCode() : 0); - return result; - } - - @Override - public String toString() { - return "BlockProxy{" + - "number='" + number + '\'' + - ", _number=" + _number + - ", hash='" + hash + '\'' + - ", parentHash='" + parentHash + '\'' + - ", stateRoot='" + stateRoot + '\'' + - ", size='" + size + '\'' + - ", _size=" + _size + - ", difficulty='" + difficulty + '\'' + - ", totalDifficulty='" + totalDifficulty + '\'' + - ", timestamp='" + timestamp + '\'' + - ", _timestamp=" + _timestamp + - ", miner='" + miner + '\'' + - ", nonce='" + nonce + '\'' + - ", extraData='" + extraData + '\'' + - ", logsBloom='" + logsBloom + '\'' + - ", mixHash='" + mixHash + '\'' + - ", gasUsed='" + gasUsed + '\'' + - ", _gasUsed=" + _gasUsed + - ", gasLimit='" + gasLimit + '\'' + - ", _gasLimit=" + _gasLimit + - ", sha3Uncles='" + sha3Uncles + '\'' + - ", uncles=" + uncles + - ", receiptsRoot='" + receiptsRoot + '\'' + - ", transactionsRoot='" + transactionsRoot + '\'' + - ", transactions=" + transactions + - '}'; - } -} diff --git a/src/main/java/io/api/etherscan/model/proxy/ReceiptProxy.java b/src/main/java/io/api/etherscan/model/proxy/ReceiptProxy.java deleted file mode 100644 index f40cb59..0000000 --- a/src/main/java/io/api/etherscan/model/proxy/ReceiptProxy.java +++ /dev/null @@ -1,143 +0,0 @@ -package io.api.etherscan.model.proxy; - -import com.google.gson.annotations.Expose; -import io.api.etherscan.model.Log; -import io.api.etherscan.util.BasicUtils; - -import java.math.BigInteger; -import java.util.List; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 03.11.2018 - */ -public class ReceiptProxy { - - private String root; - private String from; - private String to; - private String blockNumber; - @Expose(serialize = false, deserialize = false) - private Long _blockNumber; - private String blockHash; - private String transactionHash; - private String transactionIndex; - @Expose(serialize = false, deserialize = false) - private Long _transactionIndex; - private String gasUsed; - @Expose(serialize = false, deserialize = false) - private BigInteger _gasUsed; - private String cumulativeGasUsed; - @Expose(serialize = false, deserialize = false) - private BigInteger _cumulativeGasUsed; - private String contractAddress; - - private List logs; - private String logsBloom; - - // - public String getRoot() { - return root; - } - - public String getFrom() { - return from; - } - - public String getTo() { - return to; - } - - public Long getBlockNumber() { - if (_blockNumber == null && !BasicUtils.isEmpty(blockNumber)) - _blockNumber = BasicUtils.parseHex(blockNumber).longValue(); - return _blockNumber; - } - - public String getBlockHash() { - return blockHash; - } - - public String getTransactionHash() { - return transactionHash; - } - - public Long getTransactionIndex() { - if (_transactionIndex == null && !BasicUtils.isEmpty(transactionIndex)) - _transactionIndex = BasicUtils.parseHex(transactionIndex).longValue(); - return _transactionIndex; - } - - public BigInteger getGasUsed() { - if (_gasUsed == null && !BasicUtils.isEmpty(gasUsed)) - _gasUsed = BasicUtils.parseHex(gasUsed); - return _gasUsed; - } - - public BigInteger getCumulativeGasUsed() { - if (_cumulativeGasUsed == null && !BasicUtils.isEmpty(cumulativeGasUsed)) - _cumulativeGasUsed = BasicUtils.parseHex(cumulativeGasUsed); - return _cumulativeGasUsed; - } - - public String getContractAddress() { - return contractAddress; - } - - public List getLogs() { - return logs; - } - - public String getLogsBloom() { - return logsBloom; - } - // - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - - ReceiptProxy that = (ReceiptProxy) o; - - if (blockNumber != null ? !blockNumber.equals(that.blockNumber) : that.blockNumber != null) - return false; - if (transactionHash != null ? !transactionHash.equals(that.transactionHash) : that.transactionHash != null) - return false; - return transactionIndex != null ? transactionIndex.equals(that.transactionIndex) : that.transactionIndex == null; - } - - @Override - public int hashCode() { - int result = blockNumber != null ? blockNumber.hashCode() : 0; - result = 31 * result + (transactionHash != null ? transactionHash.hashCode() : 0); - result = 31 * result + (transactionIndex != null ? transactionIndex.hashCode() : 0); - return result; - } - - @Override - public String toString() { - return "ReceiptProxy{" + - "root='" + root + '\'' + - ", from='" + from + '\'' + - ", to='" + to + '\'' + - ", blockNumber='" + blockNumber + '\'' + - ", _blockNumber=" + _blockNumber + - ", blockHash='" + blockHash + '\'' + - ", transactionHash='" + transactionHash + '\'' + - ", transactionIndex='" + transactionIndex + '\'' + - ", _transactionIndex=" + _transactionIndex + - ", gasUsed='" + gasUsed + '\'' + - ", _gasUsed=" + _gasUsed + - ", cumulativeGasUsed='" + cumulativeGasUsed + '\'' + - ", _cumulativeGasUsed=" + _cumulativeGasUsed + - ", contractAddress='" + contractAddress + '\'' + - ", logs=" + logs + - ", logsBloom='" + logsBloom + '\'' + - '}'; - } -} diff --git a/src/main/java/io/api/etherscan/model/proxy/TxProxy.java b/src/main/java/io/api/etherscan/model/proxy/TxProxy.java deleted file mode 100644 index 5c7b5c8..0000000 --- a/src/main/java/io/api/etherscan/model/proxy/TxProxy.java +++ /dev/null @@ -1,157 +0,0 @@ -package io.api.etherscan.model.proxy; - -import com.google.gson.annotations.Expose; -import io.api.etherscan.util.BasicUtils; - -import java.math.BigInteger; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 31.10.2018 - */ -public class TxProxy { - - private String to; - private String hash; - private String transactionIndex; - @Expose(serialize = false, deserialize = false) - private Long _transactionIndex; - private String from; - private String v; - private String input; - private String s; - private String r; - private String nonce; - @Expose(serialize = false, deserialize = false) - private Long _nonce; - private String value; - private String gas; - @Expose(serialize = false, deserialize = false) - private BigInteger _gas; - private String gasPrice; - @Expose(serialize = false, deserialize = false) - private BigInteger _gasPrice; - private String blockHash; - private String blockNumber; - @Expose(serialize = false, deserialize = false) - private Long _blockNumber; - - // - public String getTo() { - return to; - } - - public String getHash() { - return hash; - } - - public Long getTransactionIndex() { - if (_transactionIndex == null && !BasicUtils.isEmpty(transactionIndex)) - _transactionIndex = BasicUtils.parseHex(transactionIndex).longValue(); - return _transactionIndex; - } - - public String getFrom() { - return from; - } - - public BigInteger getGas() { - if (_gas == null && !BasicUtils.isEmpty(gas)) - _gas = BasicUtils.parseHex(gas); - return _gas; - } - - public String getV() { - return v; - } - - public String getInput() { - return input; - } - - public String getS() { - return s; - } - - public String getR() { - return r; - } - - public Long getNonce() { - if (_nonce == null && !BasicUtils.isEmpty(nonce)) - _nonce = BasicUtils.parseHex(nonce).longValue(); - return _nonce; - } - - public String getValue() { - return value; - } - - public BigInteger getGasPrice() { - if (_gasPrice == null && !BasicUtils.isEmpty(gasPrice)) - _gasPrice = BasicUtils.parseHex(gasPrice); - return _gasPrice; - } - - public String getBlockHash() { - return blockHash; - } - - public Long getBlockNumber() { - if (_blockNumber == null && !BasicUtils.isEmpty(blockNumber)) - _blockNumber = BasicUtils.parseHex(blockNumber).longValue(); - return _blockNumber; - } - // - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - - TxProxy txProxy = (TxProxy) o; - - if (hash != null ? !hash.equals(txProxy.hash) : txProxy.hash != null) - return false; - if (blockHash != null ? !blockHash.equals(txProxy.blockHash) : txProxy.blockHash != null) - return false; - return blockNumber != null ? blockNumber.equals(txProxy.blockNumber) : txProxy.blockNumber == null; - } - - @Override - public int hashCode() { - int result = hash != null ? hash.hashCode() : 0; - result = 31 * result + (blockHash != null ? blockHash.hashCode() : 0); - result = 31 * result + (blockNumber != null ? blockNumber.hashCode() : 0); - return result; - } - - @Override - public String toString() { - return "TxProxy{" + - "to='" + to + '\'' + - ", hash='" + hash + '\'' + - ", transactionIndex='" + transactionIndex + '\'' + - ", _transactionIndex=" + _transactionIndex + - ", from='" + from + '\'' + - ", v='" + v + '\'' + - ", input='" + input + '\'' + - ", s='" + s + '\'' + - ", r='" + r + '\'' + - ", nonce='" + nonce + '\'' + - ", _nonce=" + _nonce + - ", value='" + value + '\'' + - ", gas='" + gas + '\'' + - ", _gas=" + _gas + - ", gasPrice='" + gasPrice + '\'' + - ", _gasPrice=" + _gasPrice + - ", blockHash='" + blockHash + '\'' + - ", blockNumber='" + blockNumber + '\'' + - ", _blockNumber=" + _blockNumber + - '}'; - } -} diff --git a/src/main/java/io/api/etherscan/model/query/IQueryBuilder.java b/src/main/java/io/api/etherscan/model/query/IQueryBuilder.java deleted file mode 100644 index 6a76c62..0000000 --- a/src/main/java/io/api/etherscan/model/query/IQueryBuilder.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.api.etherscan.model.query; - -import io.api.etherscan.error.LogQueryException; -import io.api.etherscan.model.query.impl.LogQuery; - -/** - * Builder, part of The Event Log API - * - * @author GoodforGod - * @since 31.10.2018 - */ -public interface IQueryBuilder { - - LogQuery build() throws LogQueryException; -} diff --git a/src/main/java/io/api/etherscan/model/query/impl/LogQuery.java b/src/main/java/io/api/etherscan/model/query/impl/LogQuery.java deleted file mode 100644 index 3ba6c4f..0000000 --- a/src/main/java/io/api/etherscan/model/query/impl/LogQuery.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.api.etherscan.model.query.impl; - -import io.api.etherscan.core.ILogsApi; - -/** - * Final builded container for The Event Log API - * - * EtherScan - API Descriptions https://etherscan.io/apis#logs - * - * @see LogQueryBuilder - * @see ILogsApi - * - * @author GoodforGod - * @since 31.10.2018 - */ -public class LogQuery { - - /** - * Final request parameter for api call - */ - private final String params; - - LogQuery(String params) { - this.params = params; - } - - public String getParams() { - return params; - } -} diff --git a/src/main/java/io/api/etherscan/model/query/impl/LogQueryBuilder.java b/src/main/java/io/api/etherscan/model/query/impl/LogQueryBuilder.java deleted file mode 100644 index bd8a9fc..0000000 --- a/src/main/java/io/api/etherscan/model/query/impl/LogQueryBuilder.java +++ /dev/null @@ -1,84 +0,0 @@ -package io.api.etherscan.model.query.impl; - -import io.api.etherscan.core.ILogsApi; -import io.api.etherscan.error.LogQueryException; -import io.api.etherscan.model.query.IQueryBuilder; -import io.api.etherscan.util.BasicUtils; - -/** - * Builder for The Event Log API - * - * @see ILogsApi - * - * @author GoodforGod - * @since 31.10.2018 - */ -public class LogQueryBuilder implements IQueryBuilder { - - private static final long MIN_BLOCK = 0; - private static final long MAX_BLOCK = 99999999999L; - - private final String address; - private final long startBlock, endBlock; - - private LogQueryBuilder(String address, long startBlock, long endBlock) { - this.address = address; - this.startBlock = startBlock; - this.endBlock = endBlock; - } - - public static LogQueryBuilder with(String address) { - return with(address, MIN_BLOCK); - } - - public static LogQueryBuilder with(String address, long startBlock) { - return with(address, startBlock, MAX_BLOCK); - } - - public static LogQueryBuilder with(String address, long startBlock, long endBlock) { - BasicUtils.validateAddress(address); - return new LogQueryBuilder(address, startBlock, endBlock); - } - - public LogTopicSingle topic(String topic0) { - if (BasicUtils.isNotHex(topic0)) - throw new LogQueryException("topic0 can not be empty or non hex."); - return new LogTopicSingle(address, startBlock, endBlock, topic0); - } - - public LogTopicTuple topic(String topic0, String topic1) { - if (BasicUtils.isNotHex(topic0)) - throw new LogQueryException("topic0 can not be empty or non hex."); - if (BasicUtils.isNotHex(topic1)) - throw new LogQueryException("topic1 can not be empty or non hex."); - return new LogTopicTuple(address, startBlock, endBlock, topic0, topic1); - } - - public LogTopicTriple topic(String topic0, String topic1, String topic2) { - if (BasicUtils.isNotHex(topic0)) - throw new LogQueryException("topic0 can not be empty or non hex."); - if (BasicUtils.isNotHex(topic1)) - throw new LogQueryException("topic1 can not be empty or non hex."); - if (BasicUtils.isNotHex(topic2)) - throw new LogQueryException("topic2 can not be empty or non hex."); - return new LogTopicTriple(address, startBlock, endBlock, topic0, topic1, topic2); - } - - public LogTopicQuadro topic(String topic0, String topic1, String topic2, String topic3) { - if (BasicUtils.isNotHex(topic0)) - throw new LogQueryException("topic0 can not be empty or non hex."); - if (BasicUtils.isNotHex(topic1)) - throw new LogQueryException("topic1 can not be empty or non hex."); - if (BasicUtils.isNotHex(topic2)) - throw new LogQueryException("topic2 can not be empty or non hex."); - if (BasicUtils.isNotHex(topic3)) - throw new LogQueryException("topic3 can not be empty or non hex."); - - return new LogTopicQuadro(address, startBlock, endBlock, topic0, topic1, topic2, topic3); - } - - @Override - public LogQuery build() throws LogQueryException { - return new LogQuery("&address=" + this.address + "&fromBlock=" + this.startBlock + "&toBlock=" + this.endBlock); - } -} diff --git a/src/main/java/io/api/etherscan/model/utility/BaseResponseTO.java b/src/main/java/io/api/etherscan/model/utility/BaseResponseTO.java deleted file mode 100644 index d3653e2..0000000 --- a/src/main/java/io/api/etherscan/model/utility/BaseResponseTO.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.api.etherscan.model.utility; - -import io.api.etherscan.util.BasicUtils; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 29.10.2018 - */ -public abstract class BaseResponseTO { - - private String status; - private String message; - - public int getStatus() { - return BasicUtils.isEmpty(status) ? -1 : Integer.parseInt(status); - } - - public String getMessage() { - return message; - } -} diff --git a/src/main/java/io/api/etherscan/model/utility/StringResponseTO.java b/src/main/java/io/api/etherscan/model/utility/StringResponseTO.java deleted file mode 100644 index 38d3c86..0000000 --- a/src/main/java/io/api/etherscan/model/utility/StringResponseTO.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.api.etherscan.model.utility; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 29.10.2018 - */ -public class StringResponseTO extends BaseResponseTO { - - private String result; - - public String getResult() { - return result; - } -} diff --git a/src/main/java/io/api/etherscan/model/utility/TxTokenResponseTO.java b/src/main/java/io/api/etherscan/model/utility/TxTokenResponseTO.java deleted file mode 100644 index 5ac2aec..0000000 --- a/src/main/java/io/api/etherscan/model/utility/TxTokenResponseTO.java +++ /dev/null @@ -1,13 +0,0 @@ -package io.api.etherscan.model.utility; - -import io.api.etherscan.model.TxToken; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 29.10.2018 - */ -public class TxTokenResponseTO extends BaseListResponseTO { - -} diff --git a/src/main/java/io/api/etherscan/model/utility/UncleBlockResponseTO.java b/src/main/java/io/api/etherscan/model/utility/UncleBlockResponseTO.java deleted file mode 100644 index f4f4349..0000000 --- a/src/main/java/io/api/etherscan/model/utility/UncleBlockResponseTO.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.api.etherscan.model.utility; - -import io.api.etherscan.model.UncleBlock; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 30.10.2018 - */ -public class UncleBlockResponseTO extends BaseResponseTO { - - private UncleBlock result; - - public UncleBlock getResult() { - return result; - } -} diff --git a/src/main/java/io/goodforgod/api/etherscan/AccountAPI.java b/src/main/java/io/goodforgod/api/etherscan/AccountAPI.java new file mode 100644 index 0000000..09c49eb --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/AccountAPI.java @@ -0,0 +1,216 @@ +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.model.*; +import java.util.List; +import org.jetbrains.annotations.NotNull; + +/** + * EtherScan - API Descriptions ... + * + * @author GoodforGod + * @since 28.10.2018 + */ +public interface AccountAPI { + + /** + * Address ETH balance + * + * @param address get balance for + * @return balance + * @throws EtherScanException parent exception class + */ + @NotNull + Balance balance(@NotNull String address) throws EtherScanException; + + /** + * ERC20 token balance for address + * + * @param address get balance for + * @param contract token contract + * @return token balance for address + * @throws EtherScanException parent exception class + */ + @NotNull + TokenBalance balance(@NotNull String address, @NotNull String contract) throws EtherScanException; + + /** + * Maximum 20 address for single batch request If address MORE THAN 20, then there will be more than + * 1 request performed + * + * @param addresses addresses to get balances for + * @return list of balances + * @throws EtherScanException parent exception class + */ + @NotNull + List balances(@NotNull List addresses) throws EtherScanException; + + /** + * All txs for given address + * + * @param address get txs for + * @param startBlock tx from this blockNumber + * @param endBlock tx to this blockNumber + * @return txs for address + * @throws EtherScanException parent exception class + */ + @NotNull + List txs(@NotNull String address, long startBlock, long endBlock) throws EtherScanException; + + @NotNull + List txs(@NotNull String address, long startBlock) throws EtherScanException; + + @NotNull + List txs(@NotNull String address) throws EtherScanException; + + /** + * All internal txs for given address + * + * @param address get txs for + * @param startBlock tx from this blockNumber + * @param endBlock tx to this blockNumber + * @return txs for address + * @throws EtherScanException parent exception class + */ + @NotNull + List txsInternal(@NotNull String address, long startBlock, long endBlock) throws EtherScanException; + + @NotNull + List txsInternal(@NotNull String address, long startBlock) throws EtherScanException; + + @NotNull + List txsInternal(@NotNull String address) throws EtherScanException; + + /** + * All internal tx for given transaction hash + * + * @param txhash transaction hash + * @return internal txs list + * @throws EtherScanException parent exception class + */ + @NotNull + List txsInternalByHash(@NotNull String txhash) throws EtherScanException; + + /** + * All ERC-20 token txs for given address + * + * @param address get txs for + * @param startBlock tx from this blockNumber + * @param endBlock tx to this blockNumber + * @return txs for address + * @throws EtherScanException parent exception class + */ + @NotNull + List txsErc20(@NotNull String address, long startBlock, long endBlock) throws EtherScanException; + + @NotNull + List txsErc20(@NotNull String address, long startBlock) throws EtherScanException; + + @NotNull + List txsErc20(@NotNull String address) throws EtherScanException; + + /** + * All ERC-20 token txs for given address and contract address + * + * @param address get txs for + * @param contractAddress contract address to get txs for + * @param startBlock tx from this blockNumber + * @param endBlock tx to this blockNumber + * @return txs for address + * @throws EtherScanException parent exception class + */ + @NotNull + List txsErc20(@NotNull String address, @NotNull String contractAddress, long startBlock, long endBlock) + throws EtherScanException; + + @NotNull + List txsErc20(@NotNull String address, @NotNull String contractAddress, long startBlock) throws EtherScanException; + + @NotNull + List txsErc20(@NotNull String address, @NotNull String contractAddress) throws EtherScanException; + + /** + * All ERC-721 (NFT) token txs for given address + * + * @param address get txs for + * @param startBlock tx from this blockNumber + * @param endBlock tx to this blockNumber + * @return txs for address + * @throws EtherScanException parent exception class + */ + @NotNull + List txsErc721(@NotNull String address, long startBlock, long endBlock) throws EtherScanException; + + @NotNull + List txsErc721(@NotNull String address, long startBlock) throws EtherScanException; + + @NotNull + List txsErc721(@NotNull String address) throws EtherScanException; + + /** + * All ERC-721 (NFT) token txs for given address + * + * @param address get txs for + * @param startBlock tx from this blockNumber + * @param endBlock tx to this blockNumber + * @return txs for address + * @throws EtherScanException parent exception class + */ + @NotNull + List txsErc721(@NotNull String address, @NotNull String contractAddress, long startBlock, long endBlock) + throws EtherScanException; + + @NotNull + List txsErc721(@NotNull String address, @NotNull String contractAddress, long startBlock) throws EtherScanException; + + @NotNull + List txsErc721(@NotNull String address, @NotNull String contractAddress) throws EtherScanException; + + /** + * All ERC-721 (NFT) token txs for given address + * + * @param address get txs for + * @param startBlock tx from this blockNumber + * @param endBlock tx to this blockNumber + * @return txs for address + * @throws EtherScanException parent exception class + */ + @NotNull + List txsErc1155(@NotNull String address, long startBlock, long endBlock) throws EtherScanException; + + @NotNull + List txsErc1155(@NotNull String address, long startBlock) throws EtherScanException; + + @NotNull + List txsErc1155(@NotNull String address) throws EtherScanException; + + /** + * All ERC-721 (NFT) token txs for given address + * + * @param address get txs for + * @param startBlock tx from this blockNumber + * @param endBlock tx to this blockNumber + * @return txs for address + * @throws EtherScanException parent exception class + */ + @NotNull + List txsErc1155(@NotNull String address, @NotNull String contractAddress, long startBlock, long endBlock) + throws EtherScanException; + + @NotNull + List txsErc1155(@NotNull String address, @NotNull String contractAddress, long startBlock) + throws EtherScanException; + + @NotNull + List txsErc1155(@NotNull String address, @NotNull String contractAddress) throws EtherScanException; + + /** + * All blocks mined by address + * + * @param address address to search for + * @return blocks mined + * @throws EtherScanException parent exception class + */ + @NotNull + List blocksMined(@NotNull String address) throws EtherScanException; +} diff --git a/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java new file mode 100644 index 0000000..442edff --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java @@ -0,0 +1,380 @@ +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.error.EtherScanResponseException; +import io.goodforgod.api.etherscan.http.EthHttpClient; +import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import io.goodforgod.api.etherscan.model.*; +import io.goodforgod.api.etherscan.model.response.*; +import io.goodforgod.api.etherscan.util.BasicUtils; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import org.jetbrains.annotations.NotNull; + +/** + * Account API Implementation + * + * @see AccountAPI + * @author GoodforGod + * @since 28.10.2018 + */ +final class AccountAPIProvider extends BasicProvider implements AccountAPI { + + private static final int OFFSET_MAX = 10000; + + private static final String ACT_BALANCE_ACTION = ACT_PREFIX + "balance"; + private static final String ACT_TOKEN_BALANCE_PARAM = ACT_PREFIX + "tokenbalance"; + private static final String ACT_BALANCE_MULTI_ACTION = ACT_PREFIX + "balancemulti"; + private static final String ACT_TX_ACTION = ACT_PREFIX + "txlist"; + private static final String ACT_TX_INTERNAL_ACTION = ACT_PREFIX + "txlistinternal"; + private static final String ACT_TX_ERC20_ACTION = ACT_PREFIX + "tokentx"; + private static final String ACT_TX_ERC721_ACTION = ACT_PREFIX + "tokennfttx"; + private static final String ACT_TX_ERC1155_ACTION = ACT_PREFIX + "token1155tx"; + private static final String ACT_MINED_ACTION = ACT_PREFIX + "getminedblocks"; + + private static final String BLOCK_TYPE_PARAM = "&blocktype=blocks"; + private static final String CONTRACT_PARAM = "&contractaddress="; + private static final String START_BLOCK_PARAM = "&startblock="; + private static final String TAG_LATEST_PARAM = "&tag=latest"; + private static final String END_BLOCK_PARAM = "&endblock="; + private static final String SORT_DESC_PARAM = "&sort=desc"; + private static final String SORT_ASC_PARAM = "&sort=asc"; + private static final String ADDRESS_PARAM = "&address="; + private static final String TXHASH_PARAM = "&txhash="; + private static final String OFFSET_PARAM = "&offset="; + private static final String PAGE_PARAM = "&page="; + + AccountAPIProvider(RequestQueueManager requestQueueManager, + String baseUrl, + EthHttpClient executor, + Converter converter) { + super(requestQueueManager, "account", baseUrl, executor, converter); + } + + @NotNull + @Override + public Balance balance(@NotNull String address) throws EtherScanException { + BasicUtils.validateAddress(address); + + final String urlParams = ACT_BALANCE_ACTION + TAG_LATEST_PARAM + ADDRESS_PARAM + address; + final StringResponseTO response = getRequest(urlParams, StringResponseTO.class); + if (response.getStatus() != 1) + throw new EtherScanResponseException(response); + + return new Balance(address, Wei.ofWei(new BigInteger(response.getResult()))); + } + + @NotNull + @Override + public TokenBalance balance(@NotNull String address, @NotNull String contract) throws EtherScanException { + BasicUtils.validateAddress(address); + BasicUtils.validateAddress(contract); + + final String urlParams = ACT_TOKEN_BALANCE_PARAM + ADDRESS_PARAM + address + CONTRACT_PARAM + contract; + final StringResponseTO response = getRequest(urlParams, StringResponseTO.class); + if (response.getStatus() != 1) + throw new EtherScanResponseException(response); + + return new TokenBalance(address, Wei.ofWei(new BigInteger(response.getResult())), contract); + } + + @NotNull + @Override + public List balances(@NotNull List addresses) throws EtherScanException { + if (BasicUtils.isEmpty(addresses)) { + return Collections.emptyList(); + } + + BasicUtils.validateAddresses(addresses); + + // Maximum addresses in batch request - 20 + final List balances = new ArrayList<>(); + final List> addressesAsBatches = BasicUtils.partition(addresses, 20); + + for (final List batch : addressesAsBatches) { + final String urlParams = ACT_BALANCE_MULTI_ACTION + TAG_LATEST_PARAM + ADDRESS_PARAM + toAddressParam(batch); + final BalanceResponseTO response = getRequest(urlParams, BalanceResponseTO.class); + if (response.getStatus() != 1) { + throw new EtherScanResponseException(response); + } + + if (!BasicUtils.isEmpty(response.getResult())) { + balances.addAll(response.getResult().stream() + .map(r -> new Balance(r.getAccount(), Wei.ofWei(new BigInteger(r.getBalance())))) + .collect(Collectors.toList())); + } + } + + return balances; + } + + private String toAddressParam(List addresses) { + return String.join(",", addresses); + } + + @NotNull + @Override + public List txs(@NotNull String address) throws EtherScanException { + return txs(address, MIN_START_BLOCK); + } + + @NotNull + @Override + public List txs(@NotNull String address, long startBlock) throws EtherScanException { + return txs(address, startBlock, MAX_END_BLOCK); + } + + @NotNull + @Override + public List txs(@NotNull String address, long startBlock, long endBlock) throws EtherScanException { + BasicUtils.validateAddress(address); + final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); + + final String urlParams = ACT_TX_ACTION + PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX + + ADDRESS_PARAM + address + + START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end() + + SORT_ASC_PARAM; + + return getRequestUsingOffset(urlParams, TxResponseTO.class); + } + + /** + * Generic search for txs using offset api param To avoid 10k limit per response + * + * @param urlParams Url params for #getRequest() + * @param tClass responseListTO class + * @param responseTO list T type + * @param responseListTO type + * @return List of T values + */ + private > List getRequestUsingOffset(final String urlParams, Class tClass) + throws EtherScanException { + final List result = new ArrayList<>(); + int page = 1; + while (true) { + final String formattedUrl = String.format(urlParams, page++); + final R response = getRequest(formattedUrl, tClass); + BasicUtils.validateTxResponse(response); + if (BasicUtils.isEmpty(response.getResult())) + break; + + result.addAll(response.getResult()); + if (response.getResult().size() < OFFSET_MAX) + break; + } + + return result; + } + + @NotNull + @Override + public List txsInternal(@NotNull String address) throws EtherScanException { + return txsInternal(address, MIN_START_BLOCK); + } + + @NotNull + @Override + public List txsInternal(@NotNull String address, long startBlock) throws EtherScanException { + return txsInternal(address, startBlock, MAX_END_BLOCK); + } + + @NotNull + @Override + public List txsInternal(@NotNull String address, long startBlock, long endBlock) + throws EtherScanException { + BasicUtils.validateAddress(address); + final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); + + final String urlParams = ACT_TX_INTERNAL_ACTION + PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX + + ADDRESS_PARAM + address + + START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end() + + SORT_ASC_PARAM; + + return getRequestUsingOffset(urlParams, TxInternalResponseTO.class); + } + + @NotNull + @Override + public List txsInternalByHash(@NotNull String txhash) throws EtherScanException { + BasicUtils.validateTxHash(txhash); + + final String urlParams = ACT_TX_INTERNAL_ACTION + TXHASH_PARAM + txhash; + final TxInternalResponseTO response = getRequest(urlParams, TxInternalResponseTO.class); + BasicUtils.validateTxResponse(response); + + return BasicUtils.isEmpty(response.getResult()) + ? Collections.emptyList() + : response.getResult(); + } + + @NotNull + @Override + public List txsErc20(@NotNull String address) throws EtherScanException { + return txsErc20(address, MIN_START_BLOCK); + } + + @NotNull + @Override + public List txsErc20(@NotNull String address, long startBlock) throws EtherScanException { + return txsErc20(address, startBlock, MAX_END_BLOCK); + } + + @NotNull + @Override + public List txsErc20(@NotNull String address, long startBlock, long endBlock) throws EtherScanException { + BasicUtils.validateAddress(address); + final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); + + final String urlParams = ACT_TX_ERC20_ACTION + PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX + + ADDRESS_PARAM + address + + START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end() + + SORT_ASC_PARAM; + + return getRequestUsingOffset(urlParams, TxErc20ResponseTO.class); + } + + @NotNull + @Override + public List txsErc20(@NotNull String address, @NotNull String contractAddress) throws EtherScanException { + return txsErc20(address, contractAddress, MIN_START_BLOCK); + } + + @NotNull + @Override + public List txsErc20(@NotNull String address, @NotNull String contractAddress, long startBlock) + throws EtherScanException { + return txsErc20(address, contractAddress, startBlock, MAX_END_BLOCK); + } + + @NotNull + @Override + public List txsErc20(@NotNull String address, @NotNull String contractAddress, long startBlock, long endBlock) + throws EtherScanException { + BasicUtils.validateAddress(address); + final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); + + final String offsetParam = PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX; + final String blockParam = START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end(); + final String urlParams = ACT_TX_ERC20_ACTION + offsetParam + ADDRESS_PARAM + address + + CONTRACT_PARAM + contractAddress + blockParam + SORT_ASC_PARAM; + + return getRequestUsingOffset(urlParams, TxErc20ResponseTO.class); + } + + @NotNull + @Override + public List txsErc721(@NotNull String address) throws EtherScanException { + return txsErc721(address, MIN_START_BLOCK); + } + + @NotNull + @Override + public List txsErc721(@NotNull String address, long startBlock) throws EtherScanException { + return txsErc721(address, startBlock, MAX_END_BLOCK); + } + + @NotNull + @Override + public List txsErc721(@NotNull String address, long startBlock, long endBlock) throws EtherScanException { + BasicUtils.validateAddress(address); + final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); + + final String urlParams = ACT_TX_ERC721_ACTION + PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX + + ADDRESS_PARAM + address + + START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end() + + SORT_ASC_PARAM; + + return getRequestUsingOffset(urlParams, TxErc721ResponseTO.class); + } + + @Override + public @NotNull List + txsErc721(@NotNull String address, @NotNull String contractAddress, long startBlock, long endBlock) + throws EtherScanException { + BasicUtils.validateAddress(address); + final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); + + final String offsetParam = PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX; + final String blockParam = START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end(); + final String urlParams = ACT_TX_ERC721_ACTION + offsetParam + ADDRESS_PARAM + address + + CONTRACT_PARAM + contractAddress + blockParam + SORT_ASC_PARAM; + + return getRequestUsingOffset(urlParams, TxErc721ResponseTO.class); + } + + @Override + public @NotNull List txsErc721(@NotNull String address, @NotNull String contractAddress, long startBlock) + throws EtherScanException { + return txsErc721(address, contractAddress, startBlock, MAX_END_BLOCK); + } + + @Override + public @NotNull List txsErc721(@NotNull String address, @NotNull String contractAddress) throws EtherScanException { + return txsErc721(address, contractAddress, MIN_START_BLOCK); + } + + @Override + public @NotNull List txsErc1155(@NotNull String address, long startBlock, long endBlock) + throws EtherScanException { + BasicUtils.validateAddress(address); + final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); + + final String urlParams = ACT_TX_ERC1155_ACTION + PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX + + ADDRESS_PARAM + address + + START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end() + + SORT_ASC_PARAM; + + return getRequestUsingOffset(urlParams, TxErc1155ResponseTO.class); + } + + @Override + public @NotNull List txsErc1155(@NotNull String address, long startBlock) throws EtherScanException { + return txsErc1155(address, startBlock, MAX_END_BLOCK); + } + + @Override + public @NotNull List txsErc1155(@NotNull String address) throws EtherScanException { + return txsErc1155(address, MIN_START_BLOCK); + } + + @Override + public @NotNull List + txsErc1155(@NotNull String address, @NotNull String contractAddress, long startBlock, long endBlock) + throws EtherScanException { + BasicUtils.validateAddress(address); + final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); + + final String offsetParam = PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX; + final String blockParam = START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end(); + final String urlParams = ACT_TX_ERC1155_ACTION + offsetParam + ADDRESS_PARAM + address + + CONTRACT_PARAM + contractAddress + blockParam + SORT_ASC_PARAM; + + return getRequestUsingOffset(urlParams, TxErc1155ResponseTO.class); + } + + @Override + public @NotNull List txsErc1155(@NotNull String address, @NotNull String contractAddress, long startBlock) + throws EtherScanException { + return txsErc1155(address, contractAddress, startBlock, MAX_END_BLOCK); + } + + @Override + public @NotNull List txsErc1155(@NotNull String address, @NotNull String contractAddress) + throws EtherScanException { + return txsErc1155(address, contractAddress, MIN_START_BLOCK); + } + + @NotNull + @Override + public List blocksMined(@NotNull String address) throws EtherScanException { + BasicUtils.validateAddress(address); + + final String urlParams = ACT_MINED_ACTION + PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX + BLOCK_TYPE_PARAM + + ADDRESS_PARAM + address; + + return getRequestUsingOffset(urlParams, BlockResponseTO.class); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java b/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java new file mode 100644 index 0000000..5c61aad --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java @@ -0,0 +1,88 @@ +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.error.EtherScanParseException; +import io.goodforgod.api.etherscan.error.EtherScanRateLimitException; +import io.goodforgod.api.etherscan.error.EtherScanResponseException; +import io.goodforgod.api.etherscan.http.EthHttpClient; +import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import io.goodforgod.api.etherscan.model.response.StringResponseTO; +import java.net.URI; +import java.nio.charset.StandardCharsets; + +/** + * Base provider for API Implementations + * + * @author GoodforGod + * @see EtherScanAPIProvider + * @since 28.10.2018 + */ +abstract class BasicProvider { + + private static final String MAX_RATE_LIMIT_REACHED = "Max rate limit reached"; + + static final int MAX_END_BLOCK = Integer.MAX_VALUE; + static final int MIN_START_BLOCK = 0; + + static final String ACT_PREFIX = "&action="; + + private final String module; + private final String baseUrl; + private final EthHttpClient executor; + private final RequestQueueManager queue; + private final Converter converter; + + BasicProvider(RequestQueueManager requestQueueManager, + String module, + String baseUrl, + EthHttpClient ethHttpClient, + Converter converter) { + this.queue = requestQueueManager; + this.module = "&module=" + module; + this.baseUrl = baseUrl; + this.executor = ethHttpClient; + this.converter = converter; + } + + T convert(byte[] json, Class tClass) { + try { + final T t = converter.fromJson(json, tClass); + if (t instanceof StringResponseTO && ((StringResponseTO) t).getResult().startsWith(MAX_RATE_LIMIT_REACHED)) { + throw new EtherScanRateLimitException(((StringResponseTO) t).getResult()); + } + + return t; + } catch (Exception e) { + final StringResponseTO response = converter.fromJson(json, StringResponseTO.class); + if (response.getResult() != null && response.getStatus() == 0) { + if (response.getResult().startsWith(MAX_RATE_LIMIT_REACHED)) { + throw new EtherScanRateLimitException(response.getResult()); + } else { + throw new EtherScanResponseException(response); + } + } + + final String jsonAsString = new String(json, StandardCharsets.UTF_8); + throw new EtherScanParseException(e.getMessage() + ", for response: " + jsonAsString, e.getCause(), jsonAsString); + } + } + + byte[] getRequest(String urlParameters) { + queue.takeTurn(); + final URI uri = URI.create(baseUrl + module + urlParameters); + return executor.get(uri); + } + + byte[] postRequest(String urlParameters, String dataToPost) { + queue.takeTurn(); + final URI uri = URI.create(baseUrl + module + urlParameters); + return executor.post(uri, dataToPost.getBytes(StandardCharsets.UTF_8)); + } + + T getRequest(String urlParameters, Class tClass) { + return convert(getRequest(urlParameters), tClass); + } + + T postRequest(String urlParameters, String dataToPost, Class tClass) { + return convert(postRequest(urlParameters, dataToPost), tClass); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/BlockAPI.java b/src/main/java/io/goodforgod/api/etherscan/BlockAPI.java new file mode 100644 index 0000000..fdacaf5 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/BlockAPI.java @@ -0,0 +1,25 @@ +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.model.BlockUncle; +import java.util.Optional; +import org.jetbrains.annotations.NotNull; + +/** + * EtherScan - API Descriptions ... + * + * @author GoodforGod + * @since 30.10.2018 + */ +public interface BlockAPI { + + /** + * Return uncle blocks + * + * @param blockNumber block number form 0 to last + * @return optional uncle blocks + * @throws EtherScanException parent exception class + */ + @NotNull + Optional uncles(long blockNumber) throws EtherScanException; +} diff --git a/src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java new file mode 100644 index 0000000..406ac19 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java @@ -0,0 +1,60 @@ +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.error.EtherScanResponseException; +import io.goodforgod.api.etherscan.http.EthHttpClient; +import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import io.goodforgod.api.etherscan.model.BlockUncle; +import io.goodforgod.api.etherscan.model.response.UncleBlockResponseTO; +import io.goodforgod.api.etherscan.util.BasicUtils; +import java.util.Optional; +import org.jetbrains.annotations.NotNull; + +/** + * Block API Implementation + * + * @author GoodforGod + * @see BlockAPI + * @since 28.10.2018 + */ +final class BlockAPIProvider extends BasicProvider implements BlockAPI { + + private static final String ACT_BLOCK_PARAM = ACT_PREFIX + "getblockreward"; + + private static final String BLOCKNO_PARAM = "&blockno="; + + BlockAPIProvider(RequestQueueManager requestQueueManager, + String baseUrl, + EthHttpClient executor, + Converter converter) { + super(requestQueueManager, "block", baseUrl, executor, converter); + } + + @NotNull + @Override + public Optional uncles(long blockNumber) throws EtherScanException { + final String urlParam = ACT_BLOCK_PARAM + BLOCKNO_PARAM + blockNumber; + final byte[] response = getRequest(urlParam); + if (response.length == 0) { + return Optional.empty(); + } + + try { + final UncleBlockResponseTO responseTO = convert(response, UncleBlockResponseTO.class); + if (responseTO.getMessage().startsWith("NOTOK")) { + return Optional.empty(); + } + + BasicUtils.validateTxResponse(responseTO); + return (responseTO.getResult() == null || responseTO.getResult().isEmpty()) + ? Optional.empty() + : Optional.of(responseTO.getResult()); + } catch (EtherScanResponseException e) { + if (e.getResponse().getMessage().startsWith("NOTOK")) { + return Optional.empty(); + } else { + throw e; + } + } + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/ContractAPI.java b/src/main/java/io/goodforgod/api/etherscan/ContractAPI.java new file mode 100644 index 0000000..af0852c --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/ContractAPI.java @@ -0,0 +1,24 @@ +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.model.Abi; +import org.jetbrains.annotations.NotNull; + +/** + * EtherScan - API Descriptions ... + * + * @author GoodforGod + * @since 28.10.2018 + */ +public interface ContractAPI { + + /** + * Get Verified Contract Sources + * + * @param address to verify + * @return ABI verified + * @throws EtherScanException parent exception class + */ + @NotNull + Abi contractAbi(@NotNull String address) throws EtherScanException; +} diff --git a/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java new file mode 100644 index 0000000..6b4404a --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java @@ -0,0 +1,47 @@ +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.error.EtherScanResponseException; +import io.goodforgod.api.etherscan.http.EthHttpClient; +import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import io.goodforgod.api.etherscan.model.Abi; +import io.goodforgod.api.etherscan.model.response.StringResponseTO; +import io.goodforgod.api.etherscan.util.BasicUtils; +import org.jetbrains.annotations.NotNull; + +/** + * Contract API Implementation + * + * @see ContractAPI + * @author GoodforGod + * @since 28.10.2018 + */ +final class ContractAPIProvider extends BasicProvider implements ContractAPI { + + private static final String ACT_ABI_PARAM = ACT_PREFIX + "getabi"; + + private static final String ADDRESS_PARAM = "&address="; + + ContractAPIProvider(RequestQueueManager requestQueueManager, + String baseUrl, + EthHttpClient executor, + Converter converter) { + super(requestQueueManager, "contract", baseUrl, executor, converter); + } + + @NotNull + @Override + public Abi contractAbi(@NotNull String address) throws EtherScanException { + BasicUtils.validateAddress(address); + + final String urlParam = ACT_ABI_PARAM + ADDRESS_PARAM + address; + final StringResponseTO response = getRequest(urlParam, StringResponseTO.class); + if (response.getStatus() != 1 && response.getMessage().startsWith("NOTOK")) { + throw new EtherScanResponseException(response); + } + + return (response.getResult().startsWith("Contract sou")) + ? Abi.nonVerified() + : Abi.verified(response.getResult()); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/Converter.java b/src/main/java/io/goodforgod/api/etherscan/Converter.java new file mode 100644 index 0000000..4025839 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/Converter.java @@ -0,0 +1,13 @@ +package io.goodforgod.api.etherscan; + +import org.jetbrains.annotations.NotNull; + +/** + * @author Anton Kurako (GoodforGod) + * @since 14.05.2023 + */ +public interface Converter { + + @NotNull + T fromJson(byte[] jsonAsByteArray, @NotNull Class type); +} diff --git a/src/main/java/io/goodforgod/api/etherscan/EthNetwork.java b/src/main/java/io/goodforgod/api/etherscan/EthNetwork.java new file mode 100644 index 0000000..96d8d0b --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/EthNetwork.java @@ -0,0 +1,17 @@ +package io.goodforgod.api.etherscan; + +import java.net.URI; +import org.jetbrains.annotations.NotNull; + +/** + * @author Anton Kurako (GoodforGod) + * @since 11.05.2023 + */ +public interface EthNetwork { + + /** + * @return URI for network domain like EtherScan API + */ + @NotNull + URI domain(); +} diff --git a/src/main/java/io/goodforgod/api/etherscan/EthNetworks.java b/src/main/java/io/goodforgod/api/etherscan/EthNetworks.java new file mode 100644 index 0000000..9e18508 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/EthNetworks.java @@ -0,0 +1,26 @@ +package io.goodforgod.api.etherscan; + +import java.net.URI; +import org.jetbrains.annotations.NotNull; + +/** + * @author GoodforGod + * @since 28.10.2018 + */ +public enum EthNetworks implements EthNetwork { + + MAINNET("api", "io"), + GORLI("api-goerli", "io"), + SEPOLIA("api-sepolia", "io"); + + private final URI domain; + + EthNetworks(String domain, String extension) { + this.domain = URI.create("https://" + domain + ".etherscan." + extension + "/api"); + } + + @Override + public @NotNull URI domain() { + return domain; + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java b/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java new file mode 100644 index 0000000..dad9c50 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java @@ -0,0 +1,104 @@ +package io.goodforgod.api.etherscan; + +import com.google.gson.Gson; +import io.goodforgod.api.etherscan.error.EtherScanKeyException; +import io.goodforgod.api.etherscan.error.EtherScanParseException; +import io.goodforgod.api.etherscan.http.EthHttpClient; +import io.goodforgod.api.etherscan.http.impl.UrlEthHttpClient; +import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import io.goodforgod.api.etherscan.util.BasicUtils; +import io.goodforgod.gson.configuration.GsonConfiguration; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.function.Supplier; +import org.jetbrains.annotations.NotNull; + +/** + * @author Anton Kurako (GoodforGod) + * @since 11.05.2023 + */ +final class EthScanAPIBuilder implements EtherScanAPI.Builder { + + private static final Supplier DEFAULT_SUPPLIER = UrlEthHttpClient::new; + private static final String DEFAULT_KEY = "YourApiKeyToken"; + + private final Gson gson = new GsonConfiguration().builder().create(); + + private String apiKey = DEFAULT_KEY; + private RequestQueueManager queueManager; + private EthNetwork ethNetwork = EthNetworks.MAINNET; + private Supplier ethHttpClientSupplier = DEFAULT_SUPPLIER; + private Supplier converterSupplier = () -> new Converter() { + + @Override + public @NotNull T fromJson(byte[] jsonAsByteArray, @NotNull Class type) { + try (InputStreamReader isr = new InputStreamReader(new ByteArrayInputStream(jsonAsByteArray))) { + return gson.fromJson(isr, type); + } catch (IOException e) { + throw new EtherScanParseException(e.getMessage(), e, new String(jsonAsByteArray, StandardCharsets.UTF_8)); + } + } + }; + + @NotNull + @Override + public EtherScanAPI.Builder withApiKey(@NotNull String apiKey) { + if (BasicUtils.isBlank(apiKey)) + throw new EtherScanKeyException("API key can not be null or empty"); + + this.apiKey = apiKey; + return this; + } + + @NotNull + @Override + public EtherScanAPI.Builder withNetwork(@NotNull EthNetwork network) { + this.ethNetwork = network; + return this; + } + + @NotNull + @Override + public EtherScanAPI.Builder withNetwork(@NotNull EthNetworks network) { + this.ethNetwork = network; + return this; + } + + @NotNull + @Override + public EtherScanAPI.Builder withQueue(@NotNull RequestQueueManager queueManager) { + this.queueManager = queueManager; + return this; + } + + @NotNull + @Override + public EtherScanAPI.Builder withHttpClient(@NotNull Supplier httpClientSupplier) { + this.ethHttpClientSupplier = httpClientSupplier; + return this; + } + + @NotNull + @Override + public EtherScanAPI.Builder withConverter(@NotNull Supplier converterSupplier) { + this.converterSupplier = converterSupplier; + return this; + } + + @Override + public @NotNull EtherScanAPI build() { + RequestQueueManager requestQueueManager; + if (queueManager != null) { + requestQueueManager = queueManager; + } else if (DEFAULT_KEY.equals(apiKey)) { + requestQueueManager = RequestQueueManager.anonymous(); + } else { + requestQueueManager = RequestQueueManager.planFree(); + } + + return new EtherScanAPIProvider(apiKey, ethNetwork, requestQueueManager, ethHttpClientSupplier.get(), + converterSupplier.get()); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java new file mode 100644 index 0000000..6da3d8f --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java @@ -0,0 +1,68 @@ +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.http.EthHttpClient; +import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import java.util.function.Supplier; +import org.jetbrains.annotations.NotNull; + +/** + * EtherScan full API Description ... + * + * @author GoodforGod + * @since 10.05.2023 + */ +public interface EtherScanAPI extends AutoCloseable { + + @NotNull + AccountAPI account(); + + @NotNull + ContractAPI contract(); + + @NotNull + TransactionAPI txs(); + + @NotNull + BlockAPI block(); + + @NotNull + LogsAPI logs(); + + @NotNull + ProxyAPI proxy(); + + @NotNull + StatisticAPI stats(); + + @NotNull + GasTrackerAPI gasTracker(); + + @NotNull + static Builder builder() { + return new EthScanAPIBuilder(); + } + + interface Builder { + + @NotNull + Builder withApiKey(@NotNull String apiKey); + + @NotNull + Builder withNetwork(@NotNull EthNetwork network); + + @NotNull + Builder withNetwork(@NotNull EthNetworks network); + + @NotNull + Builder withQueue(@NotNull RequestQueueManager queueManager); + + @NotNull + Builder withHttpClient(@NotNull Supplier httpClientSupplier); + + @NotNull + Builder withConverter(@NotNull Supplier converterSupplier); + + @NotNull + EtherScanAPI build(); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/EtherScanAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPIProvider.java new file mode 100644 index 0000000..e698f45 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPIProvider.java @@ -0,0 +1,95 @@ +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.http.EthHttpClient; +import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import org.jetbrains.annotations.NotNull; + +/** + * EtherScan full API Description ... + * + * @author GoodforGod + * @since 28.10.2018 + */ +final class EtherScanAPIProvider implements EtherScanAPI { + + private final RequestQueueManager requestQueueManager; + private final AccountAPI account; + private final BlockAPI block; + private final ContractAPI contract; + private final LogsAPI logs; + private final ProxyAPI proxy; + private final StatisticAPI stats; + private final TransactionAPI txs; + private final GasTrackerAPI gasTracker; + + EtherScanAPIProvider(String apiKey, + EthNetwork network, + RequestQueueManager queue, + EthHttpClient ethHttpClient, + Converter converter) { + // EtherScan 1request\5sec limit support by queue manager + final String baseUrl = network.domain() + "?apikey=" + apiKey; + + this.requestQueueManager = queue; + this.account = new AccountAPIProvider(queue, baseUrl, ethHttpClient, converter); + this.block = new BlockAPIProvider(queue, baseUrl, ethHttpClient, converter); + this.contract = new ContractAPIProvider(queue, baseUrl, ethHttpClient, converter); + this.logs = new LogsAPIProvider(queue, baseUrl, ethHttpClient, converter); + this.proxy = new ProxyAPIProvider(queue, baseUrl, ethHttpClient, converter); + this.stats = new StatisticAPIProvider(queue, baseUrl, ethHttpClient, converter); + this.txs = new TransactionAPIProvider(queue, baseUrl, ethHttpClient, converter); + this.gasTracker = new GasTrackerAPIProvider(queue, baseUrl, ethHttpClient, converter); + } + + @NotNull + @Override + public AccountAPI account() { + return account; + } + + @NotNull + @Override + public ContractAPI contract() { + return contract; + } + + @NotNull + @Override + public TransactionAPI txs() { + return txs; + } + + @NotNull + @Override + public BlockAPI block() { + return block; + } + + @NotNull + @Override + public LogsAPI logs() { + return logs; + } + + @NotNull + @Override + public ProxyAPI proxy() { + return proxy; + } + + @NotNull + @Override + public StatisticAPI stats() { + return stats; + } + + @Override + public @NotNull GasTrackerAPI gasTracker() { + return gasTracker; + } + + @Override + public void close() throws Exception { + requestQueueManager.close(); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPI.java b/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPI.java new file mode 100644 index 0000000..6fce763 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPI.java @@ -0,0 +1,35 @@ +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.model.GasOracle; +import io.goodforgod.api.etherscan.model.Wei; +import java.time.Duration; +import org.jetbrains.annotations.NotNull; + +/** + * EtherScan - API Descriptions + * ... + * + * @author Abhay Gupta + * @since 14.11.2022 + */ +public interface GasTrackerAPI { + + /** + * Returns the estimated time for a transaction to be confirmed on the blockchain. + * + * @return estimated time + * @throws EtherScanException parent exception class + */ + @NotNull + Duration estimate(@NotNull Wei wei) throws EtherScanException; + + /** + * Returns the current Safe, Proposed and Fast gas prices. + * + * @return fast, suggested gas price + * @throws EtherScanException parent exception class + */ + @NotNull + GasOracle oracle() throws EtherScanException; +} diff --git a/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java new file mode 100644 index 0000000..cbe0a75 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java @@ -0,0 +1,54 @@ +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.error.EtherScanResponseException; +import io.goodforgod.api.etherscan.http.EthHttpClient; +import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import io.goodforgod.api.etherscan.model.GasOracle; +import io.goodforgod.api.etherscan.model.Wei; +import io.goodforgod.api.etherscan.model.response.GasEstimateResponseTO; +import io.goodforgod.api.etherscan.model.response.GasOracleResponseTO; +import java.time.Duration; +import org.jetbrains.annotations.NotNull; + +/** + * GasTracker API Implementation + * + * @see GasTrackerAPI + * @author Abhay Gupta + * @since 14.11.2022 + */ +final class GasTrackerAPIProvider extends BasicProvider implements GasTrackerAPI { + + private static final String ACT_GAS_ORACLE_PARAM = ACT_PREFIX + "gasoracle"; + private static final String ACT_GAS_ESTIMATE_PARAM = ACT_PREFIX + "gasestimate"; + + private static final String GASPRICE_PARAM = "&gasprice="; + + GasTrackerAPIProvider(RequestQueueManager queue, + String baseUrl, + EthHttpClient ethHttpClient, + Converter converter) { + super(queue, "gastracker", baseUrl, ethHttpClient, converter); + } + + @Override + public @NotNull Duration estimate(@NotNull Wei wei) throws EtherScanException { + final String urlParams = ACT_GAS_ESTIMATE_PARAM + GASPRICE_PARAM + wei.asWei().toString(); + final GasEstimateResponseTO response = getRequest(urlParams, GasEstimateResponseTO.class); + if (response.getStatus() != 1) + throw new EtherScanResponseException(response); + + return Duration.ofSeconds(Long.parseLong(response.getResult())); + } + + @NotNull + @Override + public GasOracle oracle() throws EtherScanException { + final GasOracleResponseTO response = getRequest(ACT_GAS_ORACLE_PARAM, GasOracleResponseTO.class); + if (response.getStatus() != 1) + throw new EtherScanResponseException(response); + + return response.getResult(); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/LogsAPI.java b/src/main/java/io/goodforgod/api/etherscan/LogsAPI.java new file mode 100644 index 0000000..0330f9f --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/LogsAPI.java @@ -0,0 +1,27 @@ +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.model.Log; +import io.goodforgod.api.etherscan.model.query.LogQuery; +import java.util.List; +import org.jetbrains.annotations.NotNull; + +/** + * EtherScan - API Descriptions ... + * + * @author GoodforGod + * @since 30.10.2018 + */ +public interface LogsAPI { + + /** + * alternative to the native eth_getLogs Read at EtherScan API description for full info! + * + * @param query build log query + * @return logs according to query + * @throws EtherScanException parent exception class + * @see LogQuery + */ + @NotNull + List logs(@NotNull LogQuery query) throws EtherScanException; +} diff --git a/src/main/java/io/goodforgod/api/etherscan/LogsAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/LogsAPIProvider.java new file mode 100644 index 0000000..d294fb5 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/LogsAPIProvider.java @@ -0,0 +1,43 @@ +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.http.EthHttpClient; +import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import io.goodforgod.api.etherscan.model.Log; +import io.goodforgod.api.etherscan.model.query.LogQuery; +import io.goodforgod.api.etherscan.model.response.LogResponseTO; +import io.goodforgod.api.etherscan.util.BasicUtils; +import java.util.Collections; +import java.util.List; +import org.jetbrains.annotations.NotNull; + +/** + * Logs API Implementation + * + * @see LogsAPI + * @author GoodforGod + * @since 28.10.2018 + */ +final class LogsAPIProvider extends BasicProvider implements LogsAPI { + + private static final String ACT_LOGS_PARAM = ACT_PREFIX + "getLogs"; + + LogsAPIProvider(RequestQueueManager queue, + String baseUrl, + EthHttpClient executor, + Converter converter) { + super(queue, "logs", baseUrl, executor, converter); + } + + @NotNull + @Override + public List logs(@NotNull LogQuery query) throws EtherScanException { + final String urlParams = ACT_LOGS_PARAM + query.params(); + final LogResponseTO response = getRequest(urlParams, LogResponseTO.class); + BasicUtils.validateTxResponse(response); + + return (BasicUtils.isEmpty(response.getResult())) + ? Collections.emptyList() + : response.getResult(); + } +} diff --git a/src/main/java/io/api/etherscan/core/IProxyApi.java b/src/main/java/io/goodforgod/api/etherscan/ProxyAPI.java similarity index 61% rename from src/main/java/io/api/etherscan/core/IProxyApi.java rename to src/main/java/io/goodforgod/api/etherscan/ProxyAPI.java index 6adcdf0..30c4f96 100644 --- a/src/main/java/io/api/etherscan/core/IProxyApi.java +++ b/src/main/java/io/goodforgod/api/etherscan/ProxyAPI.java @@ -1,28 +1,28 @@ -package io.api.etherscan.core; +package io.goodforgod.api.etherscan; -import io.api.etherscan.error.ApiException; -import io.api.etherscan.model.proxy.BlockProxy; -import io.api.etherscan.model.proxy.ReceiptProxy; -import io.api.etherscan.model.proxy.TxProxy; +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.model.Wei; +import io.goodforgod.api.etherscan.model.proxy.BlockProxy; +import io.goodforgod.api.etherscan.model.proxy.ReceiptProxy; +import io.goodforgod.api.etherscan.model.proxy.TxProxy; +import java.util.Optional; import org.jetbrains.annotations.ApiStatus.Experimental; import org.jetbrains.annotations.NotNull; -import java.math.BigInteger; -import java.util.Optional; - /** - * EtherScan - API Descriptions https://etherscan.io/apis#proxy + * EtherScan - API Descriptions + * ... * * @author GoodforGod * @since 30.10.2018 */ -public interface IProxyApi { +public interface ProxyAPI { /** * Returns the number of most recent block eth_blockNumber * * @return last block number - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ long blockNoLast(); @@ -31,10 +31,10 @@ public interface IProxyApi { * * @param blockNo block number from 0 to last * @return optional block result - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - Optional block(long blockNo) throws ApiException; + Optional block(long blockNo) throws EtherScanException; /** * Returns information about a uncle by block number eth_getUncleByBlockNumberAndIndex @@ -42,10 +42,10 @@ public interface IProxyApi { * @param blockNo block number from 0 to last * @param index uncle block index * @return optional block result - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - Optional blockUncle(long blockNo, long index) throws ApiException; + Optional blockUncle(long blockNo, long index) throws EtherScanException; /** * Returns the information about a transaction requested by transaction hash @@ -53,10 +53,10 @@ public interface IProxyApi { * * @param txhash transaction hash * @return optional tx result - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - Optional tx(String txhash) throws ApiException; + Optional tx(@NotNull String txhash) throws EtherScanException; /** * Returns information about a transaction by block number and transaction index position @@ -65,10 +65,10 @@ public interface IProxyApi { * @param blockNo block number from 0 to last * @param index tx index in block * @return optional tx result - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - Optional tx(long blockNo, long index) throws ApiException; + Optional tx(long blockNo, long index) throws EtherScanException; /** * Returns the number of transactions in a block from a block matching the given block number @@ -76,18 +76,18 @@ public interface IProxyApi { * * @param blockNo block number from 0 to last * @return transaction amount in block - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ - int txCount(long blockNo) throws ApiException; + int txCount(long blockNo) throws EtherScanException; /** * Returns the number of transactions sent from an address eth_getTransactionCount * * @param address eth address * @return transactions send amount from address - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ - int txSendCount(String address) throws ApiException; + int txSendCount(@NotNull String address) throws EtherScanException; /** * Creates new message call transaction or a contract creation for signed transactions @@ -95,20 +95,20 @@ public interface IProxyApi { * * @param hexEncodedTx encoded hex data to send * @return optional string response - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - Optional txSendRaw(String hexEncodedTx) throws ApiException; + Optional txSendRaw(@NotNull String hexEncodedTx) throws EtherScanException; /** * Returns the receipt of a transaction by transaction hash eth_getTransactionReceipt * * @param txhash transaction hash * @return optional tx receipt - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - Optional txReceipt(String txhash) throws ApiException; + Optional txReceipt(@NotNull String txhash) throws EtherScanException; /** * Executes a new message call immediately without creating a transaction on the block chain @@ -117,20 +117,20 @@ public interface IProxyApi { * @param address to call * @param data data to call address * @return optional the return value of executed contract. - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - Optional call(String address, String data) throws ApiException; + Optional call(@NotNull String address, @NotNull String data) throws EtherScanException; /** * Returns code at a given address eth_getCode * * @param address get code from * @return optional the code from the given address - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - Optional code(String address) throws ApiException; + Optional code(@NotNull String address) throws EtherScanException; /** * (**experimental) Returns the value from a storage position at a given address eth_getStorageAt @@ -138,20 +138,20 @@ public interface IProxyApi { * @param address to get storage * @param position storage position * @return optional the value at this storage position - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @Experimental @NotNull - Optional storageAt(String address, long position) throws ApiException; + Optional storageAt(@NotNull String address, long position) throws EtherScanException; /** * Returns the current price per gas in wei eth_gasPrice * * @return estimated gas price - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - BigInteger gasPrice() throws ApiException; + Wei gasPrice() throws EtherScanException; /** * Makes a call or transaction, which won't be added to the blockchain and returns the used gas, @@ -159,11 +159,11 @@ public interface IProxyApi { * * @param hexData data to calc gas usage for * @return estimated gas usage - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - BigInteger gasEstimated(String hexData) throws ApiException; + Wei gasEstimated(@NotNull String hexData) throws EtherScanException; @NotNull - BigInteger gasEstimated() throws ApiException; + Wei gasEstimated() throws EtherScanException; } diff --git a/src/main/java/io/api/etherscan/core/impl/ProxyApiProvider.java b/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java similarity index 67% rename from src/main/java/io/api/etherscan/core/impl/ProxyApiProvider.java rename to src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java index cb0c6a5..4dff589 100644 --- a/src/main/java/io/api/etherscan/core/impl/ProxyApiProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java @@ -1,34 +1,32 @@ -package io.api.etherscan.core.impl; - -import io.api.etherscan.core.IProxyApi; -import io.api.etherscan.error.ApiException; -import io.api.etherscan.error.EtherScanException; -import io.api.etherscan.error.InvalidDataHexException; -import io.api.etherscan.executor.IHttpExecutor; -import io.api.etherscan.manager.IQueueManager; -import io.api.etherscan.model.proxy.BlockProxy; -import io.api.etherscan.model.proxy.ReceiptProxy; -import io.api.etherscan.model.proxy.TxProxy; -import io.api.etherscan.model.proxy.utility.BlockProxyTO; -import io.api.etherscan.model.proxy.utility.StringProxyTO; -import io.api.etherscan.model.proxy.utility.TxInfoProxyTO; -import io.api.etherscan.model.proxy.utility.TxProxyTO; -import io.api.etherscan.util.BasicUtils; -import org.jetbrains.annotations.NotNull; - -import java.math.BigInteger; +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.error.EtherScanInvalidDataHexException; +import io.goodforgod.api.etherscan.error.EtherScanResponseException; +import io.goodforgod.api.etherscan.http.EthHttpClient; +import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import io.goodforgod.api.etherscan.model.Wei; +import io.goodforgod.api.etherscan.model.proxy.BlockProxy; +import io.goodforgod.api.etherscan.model.proxy.ReceiptProxy; +import io.goodforgod.api.etherscan.model.proxy.TxProxy; +import io.goodforgod.api.etherscan.model.proxy.utility.BlockProxyTO; +import io.goodforgod.api.etherscan.model.proxy.utility.StringProxyTO; +import io.goodforgod.api.etherscan.model.proxy.utility.TxInfoProxyTO; +import io.goodforgod.api.etherscan.model.proxy.utility.TxProxyTO; +import io.goodforgod.api.etherscan.model.response.StringResponseTO; +import io.goodforgod.api.etherscan.util.BasicUtils; import java.util.Optional; import java.util.regex.Pattern; +import org.jetbrains.annotations.NotNull; /** * Proxy API Implementation * - * @see IProxyApi - * + * @see ProxyAPI * @author GoodforGod * @since 28.10.2018 */ -public class ProxyApiProvider extends BasicProvider implements IProxyApi { +final class ProxyAPIProvider extends BasicProvider implements ProxyAPI { private static final String ACT_BLOCKNO_PARAM = ACT_PREFIX + "eth_blockNumber"; private static final String ACT_BY_BLOCKNO_PARAM = ACT_PREFIX + "eth_getBlockByNumber"; @@ -59,14 +57,15 @@ public class ProxyApiProvider extends BasicProvider implements IProxyApi { private static final Pattern EMPTY_HEX = Pattern.compile("0x0+"); - ProxyApiProvider(final IQueueManager queue, - final String baseUrl, - final IHttpExecutor executor) { - super(queue, "proxy", baseUrl, executor); + ProxyAPIProvider(RequestQueueManager queue, + String baseUrl, + EthHttpClient executor, + Converter converter) { + super(queue, "proxy", baseUrl, executor, converter); } @Override - public long blockNoLast() throws ApiException { + public long blockNoLast() throws EtherScanException { final StringProxyTO response = getRequest(ACT_BLOCKNO_PARAM, StringProxyTO.class); return (BasicUtils.isEmpty(response.getResult())) ? -1 @@ -75,7 +74,7 @@ public long blockNoLast() throws ApiException { @NotNull @Override - public Optional block(final long blockNo) throws ApiException { + public Optional block(long blockNo) throws EtherScanException { final long compBlockNo = BasicUtils.compensateMinBlock(blockNo); final String urlParams = ACT_BY_BLOCKNO_PARAM + TAG_PARAM + compBlockNo + BOOLEAN_PARAM; @@ -85,7 +84,7 @@ public Optional block(final long blockNo) throws ApiException { @NotNull @Override - public Optional blockUncle(final long blockNo, final long index) throws ApiException { + public Optional blockUncle(long blockNo, long index) throws EtherScanException { final long compBlockNo = BasicUtils.compensateMinBlock(blockNo); final long compIndex = BasicUtils.compensateMinBlock(index); @@ -97,7 +96,7 @@ public Optional blockUncle(final long blockNo, final long index) thr @NotNull @Override - public Optional tx(final String txhash) throws ApiException { + public Optional tx(@NotNull String txhash) throws EtherScanException { BasicUtils.validateTxHash(txhash); final String urlParams = ACT_TX_BY_HASH_PARAM + TXHASH_PARAM + txhash; @@ -107,9 +106,11 @@ public Optional tx(final String txhash) throws ApiException { @NotNull @Override - public Optional tx(final long blockNo, final long index) throws ApiException { + public Optional tx(long blockNo, long index) throws EtherScanException { final long compBlockNo = BasicUtils.compensateMinBlock(blockNo); - final long compIndex = (index < 1) ? 1 : index; + final long compIndex = (index < 1) + ? 1 + : index; final String urlParams = ACT_TX_BY_BLOCKNOINDEX_PARAM + TAG_PARAM + compBlockNo + INDEX_PARAM + "0x" + Long.toHexString(compIndex); @@ -118,7 +119,7 @@ public Optional tx(final long blockNo, final long index) throws ApiExce } @Override - public int txCount(final long blockNo) throws ApiException { + public int txCount(long blockNo) throws EtherScanException { final long compensatedBlockNo = BasicUtils.compensateMinBlock(blockNo); final String urlParams = ACT_BLOCKTX_COUNT_PARAM + TAG_PARAM + "0x" + Long.toHexString(compensatedBlockNo); final StringProxyTO response = getRequest(urlParams, StringProxyTO.class); @@ -126,7 +127,7 @@ public int txCount(final long blockNo) throws ApiException { } @Override - public int txSendCount(final String address) throws ApiException { + public int txSendCount(@NotNull String address) throws EtherScanException { BasicUtils.validateAddress(address); final String urlParams = ACT_TX_COUNT_PARAM + ADDRESS_PARAM + address + TAG_LAST_PARAM; @@ -136,23 +137,30 @@ public int txSendCount(final String address) throws ApiException { @Override @NotNull - public Optional txSendRaw(final String hexEncodedTx) throws ApiException { + public Optional txSendRaw(@NotNull String hexEncodedTx) throws EtherScanException { if (BasicUtils.isNotHex(hexEncodedTx)) - throw new InvalidDataHexException("Data is not encoded in hex format - " + hexEncodedTx); + throw new EtherScanInvalidDataHexException("Data is not encoded in hex format - " + hexEncodedTx); final String urlParams = ACT_SEND_RAW_TX_PARAM + HEX_PARAM + hexEncodedTx; final StringProxyTO response = postRequest(urlParams, "", StringProxyTO.class); - if (response.getError() != null) - throw new EtherScanException("Error occurred with code " + response.getError().getCode() + if (response.getError() != null) { + final StringResponseTO responseError = StringResponseTO.builder() + .withStatus("0") + .withMessage(response.getError().getMessage()) + .withResult(response.getError().getCode()) + .build(); + + throw new EtherScanResponseException(responseError, "Error occurred with code " + response.getError().getCode() + " with message " + response.getError().getMessage() + ", error id " + response.getId() + ", jsonRPC " + response.getJsonrpc()); + } return Optional.ofNullable(response.getResult()); } @NotNull @Override - public Optional txReceipt(final String txhash) throws ApiException { + public Optional txReceipt(@NotNull String txhash) throws EtherScanException { BasicUtils.validateTxHash(txhash); final String urlParams = ACT_TX_RECEIPT_PARAM + TXHASH_PARAM + txhash; @@ -162,10 +170,10 @@ public Optional txReceipt(final String txhash) throws ApiException @NotNull @Override - public Optional call(final String address, final String data) throws ApiException { + public Optional call(@NotNull String address, @NotNull String data) throws EtherScanException { BasicUtils.validateAddress(address); if (BasicUtils.isNotHex(data)) - throw new InvalidDataHexException("Data is not hex encoded."); + throw new EtherScanInvalidDataHexException("Data is not hex encoded."); final String urlParams = ACT_CALL_PARAM + TO_PARAM + address + DATA_PARAM + data + TAG_LAST_PARAM; final StringProxyTO response = getRequest(urlParams, StringProxyTO.class); @@ -174,7 +182,7 @@ public Optional call(final String address, final String data) throws Api @NotNull @Override - public Optional code(final String address) throws ApiException { + public Optional code(@NotNull String address) throws EtherScanException { BasicUtils.validateAddress(address); final String urlParams = ACT_CODE_PARAM + ADDRESS_PARAM + address + TAG_LAST_PARAM; @@ -184,7 +192,7 @@ public Optional code(final String address) throws ApiException { @NotNull @Override - public Optional storageAt(final String address, final long position) throws ApiException { + public Optional storageAt(@NotNull String address, long position) throws EtherScanException { BasicUtils.validateAddress(address); final long compPosition = BasicUtils.compensateMinBlock(position); @@ -197,29 +205,29 @@ public Optional storageAt(final String address, final long position) thr @NotNull @Override - public BigInteger gasPrice() throws ApiException { + public Wei gasPrice() throws EtherScanException { final StringProxyTO response = getRequest(ACT_GASPRICE_PARAM, StringProxyTO.class); return (BasicUtils.isEmpty(response.getResult())) - ? BigInteger.valueOf(-1) - : BasicUtils.parseHex(response.getResult()); + ? Wei.ofWei(0) + : Wei.ofWei(BasicUtils.parseHex(response.getResult())); } @NotNull @Override - public BigInteger gasEstimated() throws ApiException { + public Wei gasEstimated() throws EtherScanException { return gasEstimated("606060405260728060106000396000f360606040526000"); } @NotNull @Override - public BigInteger gasEstimated(final String hexData) throws ApiException { + public Wei gasEstimated(@NotNull String hexData) throws EtherScanException { if (!BasicUtils.isEmpty(hexData) && BasicUtils.isNotHex(hexData)) - throw new InvalidDataHexException("Data is not in hex format."); + throw new EtherScanInvalidDataHexException("Data is not in hex format."); final String urlParams = ACT_ESTIMATEGAS_PARAM + DATA_PARAM + hexData + GAS_PARAM + "2000000000000000"; final StringProxyTO response = getRequest(urlParams, StringProxyTO.class); return (BasicUtils.isEmpty(response.getResult())) - ? BigInteger.valueOf(-1) - : BasicUtils.parseHex(response.getResult()); + ? Wei.ofWei(0) + : Wei.ofWei(BasicUtils.parseHex(response.getResult())); } } diff --git a/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java b/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java new file mode 100644 index 0000000..0a39eae --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java @@ -0,0 +1,57 @@ +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.model.EthSupply; +import io.goodforgod.api.etherscan.model.Price; +import io.goodforgod.api.etherscan.model.Wei; +import org.jetbrains.annotations.NotNull; + +/** + * EtherScan - API Descriptions ... + * + * @author GoodforGod + * @since 30.10.2018 + */ +public interface StatisticAPI { + + /** + * ERC20 token total Supply + * EtherScan + * + * @param contract contract address + * @return token supply for specified contract + * @throws EtherScanException parent exception class + */ + @NotNull + Wei supply(@NotNull String contract) throws EtherScanException; + + /** + * Returns the current amount of Ether in circulation excluding ETH2 Staking rewards and EIP1559 + * burnt fees. + * + * @return total ETH supply for moment + * @throws EtherScanException parent exception class + */ + @NotNull + Wei supply() throws EtherScanException; + + /** + * Returns the current amount of Ether in circulation, ETH2 Staking rewards, EIP1559 burnt fees, and + * total withdrawn ETH from the beacon chain. + * + * @return total ETH supply for moment + * @throws EtherScanException parent exception class + */ + @NotNull + EthSupply supplyTotal() throws EtherScanException; + + /** + * Eth last USD and BTC price + * + * @return last usd/btc price for ETH + * @throws EtherScanException parent exception class + */ + @NotNull + Price priceLast() throws EtherScanException; +} diff --git a/src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java new file mode 100644 index 0000000..131df71 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java @@ -0,0 +1,81 @@ +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.error.EtherScanResponseException; +import io.goodforgod.api.etherscan.http.EthHttpClient; +import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import io.goodforgod.api.etherscan.model.EthSupply; +import io.goodforgod.api.etherscan.model.Price; +import io.goodforgod.api.etherscan.model.Wei; +import io.goodforgod.api.etherscan.model.response.EthSupplyResponseTO; +import io.goodforgod.api.etherscan.model.response.PriceResponseTO; +import io.goodforgod.api.etherscan.model.response.StringResponseTO; +import io.goodforgod.api.etherscan.util.BasicUtils; +import java.math.BigInteger; +import org.jetbrains.annotations.NotNull; + +/** + * Statistic API Implementation + * + * @see StatisticAPI + * @author GoodforGod + * @since 28.10.2018 + */ +final class StatisticAPIProvider extends BasicProvider implements StatisticAPI { + + private static final String ACT_SUPPLY_PARAM = ACT_PREFIX + "ethsupply"; + private static final String ACT_SUPPLY2_PARAM = ACT_PREFIX + "ethsupply2"; + private static final String ACT_TOKEN_SUPPLY_PARAM = ACT_PREFIX + "tokensupply"; + private static final String ACT_LASTPRICE_PARAM = ACT_PREFIX + "ethprice"; + + private static final String CONTRACT_ADDRESS_PARAM = "&contractaddress="; + + StatisticAPIProvider(RequestQueueManager queue, + String baseUrl, + EthHttpClient executor, + Converter converter) { + super(queue, "stats", baseUrl, executor, converter); + } + + @NotNull + @Override + public Wei supply() throws EtherScanException { + final StringResponseTO response = getRequest(ACT_SUPPLY_PARAM, StringResponseTO.class); + if (response.getStatus() != 1) + throw new EtherScanResponseException(response); + + return Wei.ofWei(new BigInteger(response.getResult())); + } + + @Override + public @NotNull EthSupply supplyTotal() throws EtherScanException { + final EthSupplyResponseTO response = getRequest(ACT_SUPPLY2_PARAM, EthSupplyResponseTO.class); + if (response.getStatus() != 1) + throw new EtherScanResponseException(response); + + return response.getResult(); + } + + @NotNull + @Override + public Wei supply(@NotNull String contract) throws EtherScanException { + BasicUtils.validateAddress(contract); + + final String urlParams = ACT_TOKEN_SUPPLY_PARAM + CONTRACT_ADDRESS_PARAM + contract; + final StringResponseTO response = getRequest(urlParams, StringResponseTO.class); + if (response.getStatus() != 1) + throw new EtherScanResponseException(response); + + return Wei.ofWei(new BigInteger(response.getResult())); + } + + @NotNull + @Override + public Price priceLast() throws EtherScanException { + final PriceResponseTO response = getRequest(ACT_LASTPRICE_PARAM, PriceResponseTO.class); + if (response.getStatus() != 1) + throw new EtherScanResponseException(response); + + return response.getResult(); + } +} diff --git a/src/main/java/io/api/etherscan/core/ITransactionApi.java b/src/main/java/io/goodforgod/api/etherscan/TransactionAPI.java similarity index 50% rename from src/main/java/io/api/etherscan/core/ITransactionApi.java rename to src/main/java/io/goodforgod/api/etherscan/TransactionAPI.java index f545c2d..c719e5b 100644 --- a/src/main/java/io/api/etherscan/core/ITransactionApi.java +++ b/src/main/java/io/goodforgod/api/etherscan/TransactionAPI.java @@ -1,36 +1,35 @@ -package io.api.etherscan.core; - -import io.api.etherscan.error.ApiException; -import io.api.etherscan.model.Status; -import org.jetbrains.annotations.NotNull; +package io.goodforgod.api.etherscan; +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.model.Status; import java.util.Optional; +import org.jetbrains.annotations.NotNull; /** - * EtherScan - API Descriptions https://etherscan.io/apis#transactions + * EtherScan - API Descriptions ... * * @author GoodforGod * @since 30.10.2018 */ -public interface ITransactionApi { +public interface TransactionAPI { /** * Check Contract Execution Status (if there was an error during contract execution) * * @param txhash transaction hash * @return optional status result - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - Optional execStatus(String txhash) throws ApiException; + Optional statusExec(@NotNull String txhash) throws EtherScanException; /** * Check Transaction Receipt Status (Only applicable for Post Byzantium fork transactions) * * @param txhash transaction hash * @return 0 = Fail, 1 = Pass, empty value for pre-byzantium fork - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - Optional receiptStatus(String txhash) throws ApiException; + Optional statusReceipt(@NotNull String txhash) throws EtherScanException; } diff --git a/src/main/java/io/api/etherscan/core/impl/TransactionApiProvider.java b/src/main/java/io/goodforgod/api/etherscan/TransactionAPIProvider.java similarity index 55% rename from src/main/java/io/api/etherscan/core/impl/TransactionApiProvider.java rename to src/main/java/io/goodforgod/api/etherscan/TransactionAPIProvider.java index 82eb467..da26b51 100644 --- a/src/main/java/io/api/etherscan/core/impl/TransactionApiProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/TransactionAPIProvider.java @@ -1,40 +1,39 @@ -package io.api.etherscan.core.impl; - -import io.api.etherscan.core.ITransactionApi; -import io.api.etherscan.error.ApiException; -import io.api.etherscan.executor.IHttpExecutor; -import io.api.etherscan.manager.IQueueManager; -import io.api.etherscan.model.Status; -import io.api.etherscan.model.utility.ReceiptStatusResponseTO; -import io.api.etherscan.model.utility.StatusResponseTO; -import io.api.etherscan.util.BasicUtils; -import org.jetbrains.annotations.NotNull; - +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.http.EthHttpClient; +import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import io.goodforgod.api.etherscan.model.Status; +import io.goodforgod.api.etherscan.model.response.ReceiptStatusResponseTO; +import io.goodforgod.api.etherscan.model.response.StatusResponseTO; +import io.goodforgod.api.etherscan.util.BasicUtils; import java.util.Optional; +import org.jetbrains.annotations.NotNull; /** * Transaction API Implementation * * @author GoodforGod - * @see ITransactionApi + * @see TransactionAPI * @since 28.10.2018 */ -public class TransactionApiProvider extends BasicProvider implements ITransactionApi { +final class TransactionAPIProvider extends BasicProvider implements TransactionAPI { private static final String ACT_EXEC_STATUS_PARAM = ACT_PREFIX + "getstatus"; private static final String ACT_RECEIPT_STATUS_PARAM = ACT_PREFIX + "gettxreceiptstatus"; private static final String TXHASH_PARAM = "&txhash="; - TransactionApiProvider(final IQueueManager queue, - final String baseUrl, - final IHttpExecutor executor) { - super(queue, "transaction", baseUrl, executor); + TransactionAPIProvider(RequestQueueManager queue, + String baseUrl, + EthHttpClient executor, + Converter converter) { + super(queue, "transaction", baseUrl, executor, converter); } @NotNull @Override - public Optional execStatus(final String txhash) throws ApiException { + public Optional statusExec(@NotNull String txhash) throws EtherScanException { BasicUtils.validateTxHash(txhash); final String urlParams = ACT_EXEC_STATUS_PARAM + TXHASH_PARAM + txhash; @@ -46,7 +45,7 @@ public Optional execStatus(final String txhash) throws ApiException { @NotNull @Override - public Optional receiptStatus(final String txhash) throws ApiException { + public Optional statusReceipt(@NotNull String txhash) throws EtherScanException { BasicUtils.validateTxHash(txhash); final String urlParams = ACT_RECEIPT_STATUS_PARAM + TXHASH_PARAM + txhash; diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanConnectionException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanConnectionException.java new file mode 100644 index 0000000..731592c --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanConnectionException.java @@ -0,0 +1,16 @@ +package io.goodforgod.api.etherscan.error; + +/** + * @author GoodforGod + * @since 29.10.2018 + */ +public class EtherScanConnectionException extends EtherScanException { + + public EtherScanConnectionException(String message) { + super(message); + } + + public EtherScanConnectionException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanException.java new file mode 100644 index 0000000..67cc3bb --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanException.java @@ -0,0 +1,16 @@ +package io.goodforgod.api.etherscan.error; + +/** + * @author GoodforGod + * @since 30.10.2018 + */ +public class EtherScanException extends RuntimeException { + + public EtherScanException(String message) { + super(message); + } + + public EtherScanException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidAddressException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidAddressException.java new file mode 100644 index 0000000..ff12e3c --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidAddressException.java @@ -0,0 +1,12 @@ +package io.goodforgod.api.etherscan.error; + +/** + * @author GoodforGod + * @since 29.10.2018 + */ +public class EtherScanInvalidAddressException extends EtherScanException { + + public EtherScanInvalidAddressException(String message) { + super(message); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidDataHexException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidDataHexException.java new file mode 100644 index 0000000..b5b5f2d --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidDataHexException.java @@ -0,0 +1,12 @@ +package io.goodforgod.api.etherscan.error; + +/** + * @author GoodforGod + * @since 02.11.2018 + */ +public class EtherScanInvalidDataHexException extends EtherScanException { + + public EtherScanInvalidDataHexException(String message) { + super(message); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidTxHashException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidTxHashException.java new file mode 100644 index 0000000..33be4ad --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidTxHashException.java @@ -0,0 +1,12 @@ +package io.goodforgod.api.etherscan.error; + +/** + * @author GoodforGod + * @since 02.11.2018 + */ +public class EtherScanInvalidTxHashException extends EtherScanException { + + public EtherScanInvalidTxHashException(String message) { + super(message); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanKeyException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanKeyException.java new file mode 100644 index 0000000..f9e4aa8 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanKeyException.java @@ -0,0 +1,12 @@ +package io.goodforgod.api.etherscan.error; + +/** + * @author GoodforGod + * @since 05.11.2018 + */ +public class EtherScanKeyException extends EtherScanException { + + public EtherScanKeyException(String message) { + super(message); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanLogQueryException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanLogQueryException.java new file mode 100644 index 0000000..e72d682 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanLogQueryException.java @@ -0,0 +1,12 @@ +package io.goodforgod.api.etherscan.error; + +/** + * @author GoodforGod + * @since 31.10.2018 + */ +public class EtherScanLogQueryException extends EtherScanException { + + public EtherScanLogQueryException(String message) { + super(message); + } +} diff --git a/src/main/java/io/api/etherscan/error/ParseException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanParseException.java similarity index 52% rename from src/main/java/io/api/etherscan/error/ParseException.java rename to src/main/java/io/goodforgod/api/etherscan/error/EtherScanParseException.java index 5dc6199..87116ab 100644 --- a/src/main/java/io/api/etherscan/error/ParseException.java +++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanParseException.java @@ -1,14 +1,14 @@ -package io.api.etherscan.error; +package io.goodforgod.api.etherscan.error; /** * @author GoodforGod * @since 29.10.2018 */ -public class ParseException extends ApiException { +public class EtherScanParseException extends EtherScanException { private final String json; - public ParseException(String message, Throwable cause, String json) { + public EtherScanParseException(String message, Throwable cause, String json) { super(message, cause); this.json = json; } diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanRateLimitException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanRateLimitException.java new file mode 100644 index 0000000..eefb1ea --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanRateLimitException.java @@ -0,0 +1,12 @@ +package io.goodforgod.api.etherscan.error; + +/** + * @author iSnow + * @since 2020-10-06 + */ +public class EtherScanRateLimitException extends EtherScanException { + + public EtherScanRateLimitException(String message) { + super(message); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanResponseException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanResponseException.java new file mode 100644 index 0000000..19785ce --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanResponseException.java @@ -0,0 +1,31 @@ +package io.goodforgod.api.etherscan.error; + +import io.goodforgod.api.etherscan.model.response.BaseResponseTO; +import io.goodforgod.api.etherscan.model.response.StringResponseTO; + +/** + * @author GoodforGod + * @since 29.10.2018 + */ +public class EtherScanResponseException extends EtherScanException { + + private final transient BaseResponseTO response; + + public EtherScanResponseException(BaseResponseTO response) { + this(response, response.getMessage() + ", with status: " + response.getStatus()); + } + + public EtherScanResponseException(StringResponseTO response) { + this(response, + response.getResult() + ", with status: " + response.getStatus() + ", with message: " + response.getMessage()); + } + + public EtherScanResponseException(BaseResponseTO response, String message) { + super(message); + this.response = response; + } + + public BaseResponseTO getResponse() { + return response; + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanTimeoutException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanTimeoutException.java new file mode 100644 index 0000000..7734139 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanTimeoutException.java @@ -0,0 +1,12 @@ +package io.goodforgod.api.etherscan.error; + +/** + * @author GoodforGod + * @since 12.11.2018 + */ +public class EtherScanTimeoutException extends EtherScanConnectionException { + + public EtherScanTimeoutException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/http/EthHttpClient.java b/src/main/java/io/goodforgod/api/etherscan/http/EthHttpClient.java new file mode 100644 index 0000000..bd01f83 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/http/EthHttpClient.java @@ -0,0 +1,30 @@ +package io.goodforgod.api.etherscan.http; + +import java.net.URI; +import org.jetbrains.annotations.NotNull; + +/** + * Http Client interface + * + * @author GoodforGod + * @since 31.10.2018 + */ +public interface EthHttpClient { + + /** + * Performs a Http GET request + * + * @param uri as string + * @return result as string + */ + byte[] get(@NotNull URI uri); + + /** + * Performs a Http POST request + * + * @param uri as string + * @param body to post + * @return result as string + */ + byte[] post(@NotNull URI uri, byte[] body); +} diff --git a/src/main/java/io/goodforgod/api/etherscan/http/impl/UrlEthHttpClient.java b/src/main/java/io/goodforgod/api/etherscan/http/impl/UrlEthHttpClient.java new file mode 100644 index 0000000..b298743 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/http/impl/UrlEthHttpClient.java @@ -0,0 +1,163 @@ +package io.goodforgod.api.etherscan.http.impl; + +import static java.net.HttpURLConnection.*; + +import io.goodforgod.api.etherscan.error.EtherScanConnectionException; +import io.goodforgod.api.etherscan.error.EtherScanTimeoutException; +import io.goodforgod.api.etherscan.http.EthHttpClient; +import java.io.*; +import java.net.HttpURLConnection; +import java.net.SocketTimeoutException; +import java.net.URI; +import java.net.URL; +import java.time.Duration; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.zip.GZIPInputStream; +import java.util.zip.InflaterInputStream; +import org.jetbrains.annotations.NotNull; + +/** + * Http client implementation + * + * @author GoodforGod + * @see EthHttpClient + * @since 28.10.2018 + */ +public final class UrlEthHttpClient implements EthHttpClient { + + private static final Map DEFAULT_HEADERS = new HashMap<>(); + + private static final Duration DEFAULT_CONNECT_TIMEOUT = Duration.ofSeconds(8); + private static final Duration DEFAULT_READ_TIMEOUT = Duration.ZERO; + + static { + DEFAULT_HEADERS.put("Accept-Language", "en"); + DEFAULT_HEADERS.put("Accept-Encoding", "deflate, gzip"); + DEFAULT_HEADERS.put("User-Agent", "Chrome/68.0.3440.106"); + DEFAULT_HEADERS.put("Accept-Charset", "UTF-8"); + } + + private final Map headers; + private final int connectTimeout; + private final int readTimeout; + + public UrlEthHttpClient() { + this(DEFAULT_CONNECT_TIMEOUT); + } + + public UrlEthHttpClient(Duration connectTimeout) { + this(connectTimeout, DEFAULT_READ_TIMEOUT); + } + + public UrlEthHttpClient(Duration connectTimeout, Duration readTimeout) { + this(connectTimeout, readTimeout, DEFAULT_HEADERS); + } + + /** + * @param connectTimeout custom connection establish timeout in millis + * @param readTimeout custom read timeout in millis + * @param headers custom HTTP headers + */ + public UrlEthHttpClient(Duration connectTimeout, + Duration readTimeout, + Map headers) { + this.connectTimeout = Math.toIntExact(connectTimeout.toMillis()); + this.readTimeout = Math.toIntExact(readTimeout.toMillis()); + this.headers = Collections.unmodifiableMap(headers); + } + + private HttpURLConnection buildConnection(URI uri, String method) throws IOException { + final URL url = uri.toURL(); + final HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod(method); + connection.setConnectTimeout(connectTimeout); + connection.setReadTimeout(readTimeout); + headers.forEach(connection::setRequestProperty); + return connection; + } + + @Override + public byte[] get(@NotNull URI uri) { + try { + final HttpURLConnection connection = buildConnection(uri, "GET"); + final int status = connection.getResponseCode(); + if (status == HTTP_MOVED_TEMP || status == HTTP_MOVED_PERM) { + return get(URI.create(connection.getHeaderField("Location"))); + } else if ((status >= HTTP_BAD_REQUEST) && (status < HTTP_INTERNAL_ERROR)) { + throw new EtherScanConnectionException("Protocol error: " + connection.getResponseMessage()); + } else if (status >= HTTP_INTERNAL_ERROR) { + throw new EtherScanConnectionException("Server error: " + connection.getResponseMessage()); + } + + final byte[] data = readData(connection); + connection.disconnect(); + return data; + } catch (SocketTimeoutException e) { + throw new EtherScanTimeoutException("Timeout: Could not establish connection for " + connectTimeout + " millis", e); + } catch (Exception e) { + throw new EtherScanConnectionException(e.getMessage(), e); + } + } + + @Override + public byte[] post(@NotNull URI uri, byte[] body) { + try { + final HttpURLConnection connection = buildConnection(uri, "POST"); + final int contentLength = body.length; + connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); + connection.setRequestProperty("Content-Length", String.valueOf(contentLength)); + connection.setFixedLengthStreamingMode(body.length); + + connection.setDoOutput(true); + connection.connect(); + try (OutputStream os = connection.getOutputStream()) { + os.write(body); + } + + final int status = connection.getResponseCode(); + if (status == HTTP_MOVED_TEMP || status == HTTP_MOVED_PERM) { + return post(URI.create(connection.getHeaderField("Location")), body); + } else if ((status >= HTTP_BAD_REQUEST) && (status < HTTP_INTERNAL_ERROR)) { + throw new EtherScanConnectionException("Protocol error: " + connection.getResponseMessage()); + } else if (status >= HTTP_INTERNAL_ERROR) { + throw new EtherScanConnectionException("Server error: " + connection.getResponseMessage()); + } + + final byte[] data = readData(connection); + connection.disconnect(); + return data; + } catch (SocketTimeoutException e) { + throw new EtherScanTimeoutException("Timeout: Could not establish connection for " + connectTimeout + " millis", e); + } catch (Exception e) { + throw new EtherScanConnectionException(e.getMessage(), e); + } + } + + private byte[] readData(HttpURLConnection connection) throws IOException { + try (ByteArrayOutputStream buffer = new ByteArrayOutputStream()) { + try (InputStream in = getStreamReader(connection)) { + byte[] data = new byte[256]; + int nRead; + while ((nRead = in.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, nRead); + } + } + + buffer.flush(); + return buffer.toByteArray(); + } + } + + private InputStream getStreamReader(HttpURLConnection connection) throws IOException { + switch (String.valueOf(connection.getContentEncoding())) { + case "gzip": + return new GZIPInputStream(connection.getInputStream()); + case "deflate": + return new InflaterInputStream(connection.getInputStream()); + default: + return connection.getInputStream(); + } + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java b/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java new file mode 100644 index 0000000..0f36b23 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java @@ -0,0 +1,51 @@ +package io.goodforgod.api.etherscan.manager; + +import io.goodforgod.api.etherscan.manager.impl.FakeRequestQueueManager; +import io.goodforgod.api.etherscan.manager.impl.SemaphoreRequestQueueManager; +import java.time.Duration; + +/** + * Queue manager to support API limits + * Manager grants turn if the limit is not exhausted And resets queue each set period + * + * @author GoodforGod + * @since 30.10.2018 + */ +public interface RequestQueueManager extends AutoCloseable { + + /** + * Is used by default when no API KEY is provided + */ + static RequestQueueManager anonymous() { + return new SemaphoreRequestQueueManager(1, Duration.ofMillis(5015L)); + } + + /** + * Is available for all registered free API KEYs + * Free API KEY + */ + static RequestQueueManager planFree() { + return new SemaphoreRequestQueueManager(5, Duration.ofMillis(1015L)); + } + + static RequestQueueManager planStandard() { + return new SemaphoreRequestQueueManager(10, Duration.ofMillis(1015L)); + } + + static RequestQueueManager planAdvanced() { + return new SemaphoreRequestQueueManager(20, Duration.ofMillis(1015L)); + } + + static RequestQueueManager planProfessional() { + return new SemaphoreRequestQueueManager(30, Duration.ofMillis(1015L)); + } + + static RequestQueueManager unlimited() { + return new FakeRequestQueueManager(); + } + + /** + * Waits in queue for chance to take turn + */ + void takeTurn(); +} diff --git a/src/main/java/io/api/etherscan/manager/impl/FakeQueueManager.java b/src/main/java/io/goodforgod/api/etherscan/manager/impl/FakeRequestQueueManager.java similarity index 65% rename from src/main/java/io/api/etherscan/manager/impl/FakeQueueManager.java rename to src/main/java/io/goodforgod/api/etherscan/manager/impl/FakeRequestQueueManager.java index 620244c..626b4c1 100644 --- a/src/main/java/io/api/etherscan/manager/impl/FakeQueueManager.java +++ b/src/main/java/io/goodforgod/api/etherscan/manager/impl/FakeRequestQueueManager.java @@ -1,6 +1,6 @@ -package io.api.etherscan.manager.impl; +package io.goodforgod.api.etherscan.manager.impl; -import io.api.etherscan.manager.IQueueManager; +import io.goodforgod.api.etherscan.manager.RequestQueueManager; /** * Fake queue manager, always give turns, when you have no limits @@ -8,7 +8,7 @@ * @author GoodforGod * @since 03.11.2018 */ -public class FakeQueueManager implements IQueueManager { +public final class FakeRequestQueueManager implements RequestQueueManager { @Override public void takeTurn() { diff --git a/src/main/java/io/goodforgod/api/etherscan/manager/impl/SemaphoreRequestQueueManager.java b/src/main/java/io/goodforgod/api/etherscan/manager/impl/SemaphoreRequestQueueManager.java new file mode 100644 index 0000000..2a3483c --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/manager/impl/SemaphoreRequestQueueManager.java @@ -0,0 +1,54 @@ +package io.goodforgod.api.etherscan.manager.impl; + +import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import java.time.Duration; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; + +/** + * Queue Semaphore implementation with size and reset time as params + * + * @see RequestQueueManager + * @author GoodforGod + * @since 30.10.2018 + */ +public final class SemaphoreRequestQueueManager implements RequestQueueManager, AutoCloseable { + + private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); + private final Semaphore semaphore; + private final long queueResetTimeInMillis; + + public SemaphoreRequestQueueManager(int size, Duration resetIn) { + this.semaphore = new Semaphore(0); + this.queueResetTimeInMillis = resetIn.toMillis(); + this.executorService.scheduleAtFixedRate(releaseLocks(size), + resetIn.toMillis(), queueResetTimeInMillis, TimeUnit.MILLISECONDS); + } + + @SuppressWarnings("java:S899") + @Override + public void takeTurn() { + try { + semaphore.tryAcquire(queueResetTimeInMillis, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + private Runnable releaseLocks(int toRelease) { + return () -> { + int availablePermits = semaphore.availablePermits(); + int neededPermits = toRelease - availablePermits; + if (neededPermits > 0) { + semaphore.release(neededPermits); + } + }; + } + + @Override + public void close() { + executorService.shutdown(); + } +} diff --git a/src/main/java/io/api/etherscan/model/Abi.java b/src/main/java/io/goodforgod/api/etherscan/model/Abi.java similarity index 51% rename from src/main/java/io/api/etherscan/model/Abi.java rename to src/main/java/io/goodforgod/api/etherscan/model/Abi.java index a48a11d..3536bf9 100644 --- a/src/main/java/io/api/etherscan/model/Abi.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Abi.java @@ -1,17 +1,16 @@ -package io.api.etherscan.model; +package io.goodforgod.api.etherscan.model; -import io.api.etherscan.util.BasicUtils; +import io.goodforgod.api.etherscan.util.BasicUtils; +import java.util.Objects; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 31.10.2018 */ public class Abi { - private String contractAbi; - private boolean isVerified; + private final String contractAbi; + private final boolean isVerified; private Abi(String contractAbi, boolean isVerified) { this.contractAbi = contractAbi; @@ -42,21 +41,15 @@ public boolean isVerified() { public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) + if (!(o instanceof Abi)) return false; - Abi abi = (Abi) o; - - if (isVerified != abi.isVerified) - return false; - return contractAbi != null ? contractAbi.equals(abi.contractAbi) : abi.contractAbi == null; + return isVerified == abi.isVerified && Objects.equals(contractAbi, abi.contractAbi); } @Override public int hashCode() { - int result = contractAbi != null ? contractAbi.hashCode() : 0; - result = 31 * result + (isVerified ? 1 : 0); - return result; + return Objects.hash(contractAbi, isVerified); } @Override @@ -66,4 +59,30 @@ public String toString() { ", isVerified=" + isVerified + '}'; } + + public static AbiBuilder builder() { + return new AbiBuilder(); + } + + public static final class AbiBuilder { + + private String contractAbi; + private boolean isVerified; + + private AbiBuilder() {} + + public AbiBuilder withContractAbi(String contractAbi) { + this.contractAbi = contractAbi; + return this; + } + + public AbiBuilder withIsVerified(boolean isVerified) { + this.isVerified = isVerified; + return this; + } + + public Abi build() { + return new Abi(contractAbi, isVerified); + } + } } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Balance.java b/src/main/java/io/goodforgod/api/etherscan/model/Balance.java new file mode 100644 index 0000000..079d4b6 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/Balance.java @@ -0,0 +1,52 @@ +package io.goodforgod.api.etherscan.model; + +import java.util.Objects; + +/** + * @author GoodforGod + * @since 28.10.2018 + */ +public class Balance { + + /** Balance in Wei */ + private final Wei balance; + private final String address; + + public Balance(String address, Wei balance) { + this.address = address; + this.balance = balance; + } + + // + public String getAddress() { + return address; + } + + public Wei getBalanceInWei() { + return balance; + } + // + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof Balance)) + return false; + Balance balance1 = (Balance) o; + return Objects.equals(balance, balance1.balance) && Objects.equals(address, balance1.address); + } + + @Override + public int hashCode() { + return Objects.hash(balance, address); + } + + @Override + public String toString() { + return "Balance{" + + "address='" + address + '\'' + + ", balance=" + balance + + '}'; + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/BaseTx.java b/src/main/java/io/goodforgod/api/etherscan/model/BaseTx.java new file mode 100644 index 0000000..8de679a --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/BaseTx.java @@ -0,0 +1,91 @@ +package io.goodforgod.api.etherscan.model; + +import com.google.gson.annotations.Expose; +import io.goodforgod.api.etherscan.util.BasicUtils; +import java.math.BigInteger; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.Objects; +import org.jetbrains.annotations.NotNull; + +/** + * @author GoodforGod + * @since 28.10.2018 + */ +abstract class BaseTx implements Comparable { + + long blockNumber; + String timeStamp; + @Expose(deserialize = false, serialize = false) + LocalDateTime _timeStamp; + String hash; + String from; + String to; + String contractAddress; + String input; + BigInteger gas; + BigInteger gasUsed; + + // + public long getBlockNumber() { + return blockNumber; + } + + public LocalDateTime getTimeStamp() { + if (_timeStamp == null && !BasicUtils.isEmpty(timeStamp)) + _timeStamp = LocalDateTime.ofEpochSecond(Long.parseLong(timeStamp), 0, ZoneOffset.UTC); + return _timeStamp; + } + + public String getHash() { + return hash; + } + + public String getFrom() { + return from; + } + + public String getTo() { + return to; + } + + public String getContractAddress() { + return contractAddress; + } + + public String getInput() { + return input; + } + + public Wei getGas() { + return Wei.ofWei(gas); + } + + public Wei getGasUsed() { + return Wei.ofWei(gasUsed); + } + // + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof BaseTx)) + return false; + BaseTx baseTx = (BaseTx) o; + return blockNumber == baseTx.blockNumber && Objects.equals(timeStamp, baseTx.timeStamp) + && Objects.equals(hash, baseTx.hash) && Objects.equals(from, baseTx.from) && Objects.equals(to, baseTx.to) + && Objects.equals(contractAddress, baseTx.contractAddress) && Objects.equals(input, baseTx.input) + && Objects.equals(gas, baseTx.gas) && Objects.equals(gasUsed, baseTx.gasUsed); + } + + @Override + public int hashCode() { + return Objects.hash(blockNumber, timeStamp, hash, from, to, contractAddress, input, gas, gasUsed); + } + + @Override + public int compareTo(@NotNull BaseTx o) { + return Long.compare(blockNumber, o.blockNumber); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Block.java b/src/main/java/io/goodforgod/api/etherscan/model/Block.java new file mode 100644 index 0000000..0550000 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/Block.java @@ -0,0 +1,108 @@ +package io.goodforgod.api.etherscan.model; + +import com.google.gson.annotations.Expose; +import io.goodforgod.api.etherscan.util.BasicUtils; +import java.math.BigInteger; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.Objects; +import org.jetbrains.annotations.NotNull; + +/** + * @author GoodforGod + * @since 28.10.2018 + */ +public class Block implements Comparable { + + long blockNumber; + BigInteger blockReward; + String timeStamp; + @Expose(deserialize = false, serialize = false) + LocalDateTime _timeStamp; + + protected Block() {} + + // + public long getBlockNumber() { + return blockNumber; + } + + public LocalDateTime getTimeStamp() { + if (_timeStamp == null && !BasicUtils.isEmpty(timeStamp)) + _timeStamp = LocalDateTime.ofEpochSecond(Long.parseLong(timeStamp), 0, ZoneOffset.UTC); + return _timeStamp; + } + + public BigInteger getBlockReward() { + return blockReward; + } + // + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof Block)) + return false; + Block block = (Block) o; + return blockNumber == block.blockNumber; + } + + @Override + public int hashCode() { + return Objects.hash(blockNumber); + } + + @Override + public String toString() { + return "Block{" + + "blockNumber=" + blockNumber + + ", blockReward=" + blockReward + + ", timeStamp='" + timeStamp + '\'' + + '}'; + } + + @Override + public int compareTo(@NotNull Block o) { + return Long.compare(blockNumber, o.blockNumber); + } + + public static BlockBuilder builder() { + return new BlockBuilder(); + } + + public static class BlockBuilder { + + private long blockNumber; + private BigInteger blockReward; + private LocalDateTime timeStamp; + + BlockBuilder() {} + + public BlockBuilder withBlockNumber(long blockNumber) { + this.blockNumber = blockNumber; + return this; + } + + public BlockBuilder withBlockReward(BigInteger blockReward) { + this.blockReward = blockReward; + return this; + } + + public BlockBuilder withTimeStamp(LocalDateTime timeStamp) { + this.timeStamp = timeStamp; + return this; + } + + public Block build() { + Block block = new Block(); + block.blockNumber = this.blockNumber; + block.blockReward = this.blockReward; + if (this.timeStamp != null) { + block._timeStamp = this.timeStamp; + block.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + } + return block; + } + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/BlockTx.java b/src/main/java/io/goodforgod/api/etherscan/model/BlockTx.java new file mode 100644 index 0000000..f3c4d67 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/BlockTx.java @@ -0,0 +1,79 @@ +package io.goodforgod.api.etherscan.model; + +import java.math.BigInteger; +import java.util.Objects; +import org.jetbrains.annotations.NotNull; + +/** + * @author Anton Kurako (GoodforGod) + * @since 15.05.2023 + */ +abstract class BlockTx extends BaseTx { + + long nonce; + String blockHash; + long transactionIndex; + long confirmations; + BigInteger gasPrice; + BigInteger cumulativeGasUsed; + + public long getNonce() { + return nonce; + } + + public String getBlockHash() { + return blockHash; + } + + public long getTransactionIndex() { + return transactionIndex; + } + + public Wei getGasPrice() { + return Wei.ofWei(gasPrice); + } + + public Wei getGasUsedCumulative() { + return Wei.ofWei(cumulativeGasUsed); + } + + public long getConfirmations() { + return confirmations; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof BlockTx)) + return false; + if (!super.equals(o)) + return false; + BlockTx blockTx = (BlockTx) o; + return nonce == blockTx.nonce && transactionIndex == blockTx.transactionIndex + && Objects.equals(blockHash, blockTx.blockHash); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), nonce, blockHash, transactionIndex); + } + + @Override + public int compareTo(@NotNull BaseTx o) { + final int superCompare = super.compareTo(o); + if (superCompare == 0) { + if (o instanceof Tx) { + return Long.compare(transactionIndex, ((Tx) o).getTransactionIndex()); + } else if (o instanceof TxErc20) { + return Long.compare(transactionIndex, ((TxErc20) o).getTransactionIndex()); + } else if (o instanceof TxErc721) { + return Long.compare(transactionIndex, ((TxErc721) o).getTransactionIndex()); + } else if (o instanceof TxErc1155) { + return Long.compare(transactionIndex, ((TxErc1155) o).getTransactionIndex()); + } + } + + return superCompare; + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java b/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java new file mode 100644 index 0000000..9b110d9 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java @@ -0,0 +1,198 @@ +package io.goodforgod.api.etherscan.model; + +import io.goodforgod.api.etherscan.util.BasicUtils; +import java.math.BigInteger; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.List; +import java.util.Objects; + +/** + * @author GoodforGod + * @since 30.10.2018 + */ +public class BlockUncle extends Block { + + public static class Uncle { + + private String miner; + private BigInteger blockreward; + private int unclePosition; + + private Uncle() {} + + // + public String getMiner() { + return miner; + } + + public BigInteger getBlockreward() { + return blockreward; + } + + public int getUnclePosition() { + return unclePosition; + } + // + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof Uncle)) + return false; + Uncle uncle = (Uncle) o; + return unclePosition == uncle.unclePosition && Objects.equals(miner, uncle.miner) + && Objects.equals(blockreward, uncle.blockreward); + } + + @Override + public int hashCode() { + return Objects.hash(miner, blockreward, unclePosition); + } + + @Override + public String toString() { + return "Uncle{" + + "miner='" + miner + '\'' + + ", blockreward=" + blockreward + + ", unclePosition=" + unclePosition + + '}'; + } + + public static UncleBuilder builder() { + return new UncleBuilder(); + } + + public static final class UncleBuilder { + + private String miner; + private BigInteger blockreward; + private int unclePosition; + + private UncleBuilder() {} + + public UncleBuilder withMiner(String miner) { + this.miner = miner; + return this; + } + + public UncleBuilder withBlockreward(BigInteger blockreward) { + this.blockreward = blockreward; + return this; + } + + public UncleBuilder withUnclePosition(int unclePosition) { + this.unclePosition = unclePosition; + return this; + } + + public Uncle build() { + Uncle uncle = new Uncle(); + uncle.miner = this.miner; + uncle.blockreward = this.blockreward; + uncle.unclePosition = this.unclePosition; + return uncle; + } + } + } + + private String blockMiner; + private List uncles; + private String uncleInclusionReward; + + protected BlockUncle() { + super(); + } + + // + public boolean isEmpty() { + return getBlockNumber() == 0 && getBlockReward() == null + && getTimeStamp() == null + && BasicUtils.isEmpty(blockMiner); + } + + public String getBlockMiner() { + return blockMiner; + } + + public List getUncles() { + return uncles; + } + + public String getUncleInclusionReward() { + return uncleInclusionReward; + } + // + + @Override + public String toString() { + return "UncleBlock{" + + "blockMiner='" + blockMiner + '\'' + + ", uncles=" + uncles + + ", uncleInclusionReward='" + uncleInclusionReward + '\'' + + '}'; + } + + public static BlockUncleBuilder builder() { + return new BlockUncleBuilder(); + } + + public static final class BlockUncleBuilder extends Block.BlockBuilder { + + private long blockNumber; + private BigInteger blockReward; + private LocalDateTime timeStamp; + private String blockMiner; + private List uncles; + private String uncleInclusionReward; + + private BlockUncleBuilder() { + super(); + } + + public BlockUncleBuilder withBlockNumber(long blockNumber) { + this.blockNumber = blockNumber; + return this; + } + + public BlockUncleBuilder withBlockReward(BigInteger blockReward) { + this.blockReward = blockReward; + return this; + } + + public BlockUncleBuilder withTimeStamp(LocalDateTime timeStamp) { + this.timeStamp = timeStamp; + return this; + } + + public BlockUncleBuilder withBlockMiner(String blockMiner) { + this.blockMiner = blockMiner; + return this; + } + + public BlockUncleBuilder withUncles(List uncles) { + this.uncles = uncles; + return this; + } + + public BlockUncleBuilder withUncleInclusionReward(String uncleInclusionReward) { + this.uncleInclusionReward = uncleInclusionReward; + return this; + } + + public BlockUncle build() { + BlockUncle blockUncle = new BlockUncle(); + blockUncle.uncles = this.uncles; + blockUncle.uncleInclusionReward = this.uncleInclusionReward; + blockUncle.blockNumber = this.blockNumber; + blockUncle.blockReward = this.blockReward; + blockUncle.blockMiner = this.blockMiner; + if (this.timeStamp != null) { + blockUncle._timeStamp = this.timeStamp; + blockUncle.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + } + return blockUncle; + } + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/EthSupply.java b/src/main/java/io/goodforgod/api/etherscan/model/EthSupply.java new file mode 100644 index 0000000..344e754 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/EthSupply.java @@ -0,0 +1,124 @@ +package io.goodforgod.api.etherscan.model; + +import java.math.BigInteger; +import java.util.Objects; + +/** + * @author Anton Kurako (GoodforGod) + * @since 14.05.2023 + */ +public class EthSupply { + + private String EthSupply; + private String Eth2Staking; + private String BurntFees; + private String WithdrawnTotal; + + public Wei getEthSupply() { + return Wei.ofWei(new BigInteger(EthSupply)); + } + + public Wei getEth2Staking() { + return Wei.ofWei(new BigInteger(Eth2Staking)); + } + + public Wei getBurntFees() { + return Wei.ofWei(new BigInteger(BurntFees)); + } + + public Wei getTotal() { + final BigInteger total = getEthSupply().asWei() + .add(getEth2Staking().asWei()) + .min(getBurntFees().asWei()); + return Wei.ofWei(total); + } + + public Wei getWithdrawnTotal() { + return Wei.ofWei(new BigInteger(WithdrawnTotal)); + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof EthSupply)) + return false; + EthSupply ethSupply = (EthSupply) o; + return Objects.equals(EthSupply, ethSupply.EthSupply) && Objects.equals(Eth2Staking, ethSupply.Eth2Staking) + && Objects.equals(BurntFees, ethSupply.BurntFees) && Objects.equals(WithdrawnTotal, ethSupply.WithdrawnTotal); + } + + @Override + public int hashCode() { + return Objects.hash(EthSupply, Eth2Staking, BurntFees, WithdrawnTotal); + } + + @Override + public String toString() { + return "EthSupply{" + + "EthSupply='" + EthSupply + '\'' + + ", Eth2Staking='" + Eth2Staking + '\'' + + ", BurntFees='" + BurntFees + '\'' + + ", WithdrawnTotal='" + WithdrawnTotal + '\'' + + '}'; + } + + public static EthSupplyBuilder builder() { + return new EthSupplyBuilder(); + } + + public static final class EthSupplyBuilder { + + private Wei ethSupply; + private Wei eth2Staking; + private Wei burntFees; + private Wei withdrawnTotal; + + private EthSupplyBuilder() {} + + public EthSupplyBuilder withEthSupply(Wei ethSupply) { + this.ethSupply = ethSupply; + return this; + } + + public EthSupplyBuilder withEth2Staking(Wei eth2Staking) { + this.eth2Staking = eth2Staking; + return this; + } + + public EthSupplyBuilder withBurntFees(Wei burntFees) { + this.burntFees = burntFees; + return this; + } + + public EthSupplyBuilder withWithdrawnTotal(Wei withdrawnTotal) { + this.withdrawnTotal = withdrawnTotal; + return this; + } + + public EthSupply build() { + EthSupply ethSupply = new EthSupply(); + if (this.burntFees != null) { + ethSupply.BurntFees = this.burntFees.toString(); + } else { + ethSupply.BurntFees = BigInteger.ZERO.toString(); + } + if (this.eth2Staking != null) { + ethSupply.Eth2Staking = this.eth2Staking.toString(); + } else { + ethSupply.Eth2Staking = BigInteger.ZERO.toString(); + } + if (this.ethSupply != null) { + ethSupply.EthSupply = this.ethSupply.toString(); + } else { + ethSupply.EthSupply = BigInteger.ZERO.toString(); + } + if (this.withdrawnTotal != null) { + ethSupply.WithdrawnTotal = this.withdrawnTotal.toString(); + } else { + ethSupply.WithdrawnTotal = BigInteger.ZERO.toString(); + } + return ethSupply; + } + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java b/src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java new file mode 100644 index 0000000..6fe1231 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java @@ -0,0 +1,148 @@ +package io.goodforgod.api.etherscan.model; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * @author Abhay Gupta + * @since 14.11.2022 + */ +public class GasOracle { + + private Long LastBlock; + private BigInteger SafeGasPrice; + private BigInteger ProposeGasPrice; + private BigInteger FastGasPrice; + private BigDecimal suggestBaseFee; + private String gasUsedRatio; + + protected GasOracle() {} + + public Long getLastBlock() { + return LastBlock; + } + + public Wei getSafeGasPriceInWei() { + return Wei.ofGwei(SafeGasPrice); + } + + public Wei getProposeGasPriceInWei() { + return Wei.ofGwei(ProposeGasPrice); + } + + public Wei getFastGasPriceInWei() { + return Wei.ofGwei(FastGasPrice); + } + + public BigDecimal getSuggestBaseFee() { + return suggestBaseFee; + } + + public List getGasUsedRatio() { + return Arrays.stream(gasUsedRatio.split(",")) + .map(BigDecimal::new) + .collect(Collectors.toList()); + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof GasOracle)) + return false; + GasOracle gasOracle = (GasOracle) o; + return Objects.equals(LastBlock, gasOracle.LastBlock) && Objects.equals(SafeGasPrice, gasOracle.SafeGasPrice) + && Objects.equals(ProposeGasPrice, gasOracle.ProposeGasPrice) + && Objects.equals(FastGasPrice, gasOracle.FastGasPrice) + && Objects.equals(suggestBaseFee, gasOracle.suggestBaseFee) + && Objects.equals(gasUsedRatio, gasOracle.gasUsedRatio); + } + + @Override + public int hashCode() { + return Objects.hash(LastBlock, SafeGasPrice, ProposeGasPrice, FastGasPrice, suggestBaseFee, gasUsedRatio); + } + + @Override + public String toString() { + return "GasOracle{" + + "LastBlock=" + LastBlock + + ", SafeGasPrice=" + SafeGasPrice + + ", ProposeGasPrice=" + ProposeGasPrice + + ", FastGasPrice=" + FastGasPrice + + ", suggestBaseFee=" + suggestBaseFee + + ", gasUsedRatio=" + gasUsedRatio + + '}'; + } + + public static GasOracleBuilder builder() { + return new GasOracleBuilder(); + } + + public static final class GasOracleBuilder { + + private Long lastBlock; + private Wei safeGasPrice; + private Wei proposeGasPrice; + private Wei fastGasPrice; + private BigDecimal suggestBaseFee; + private List gasUsedRatio; + + private GasOracleBuilder() {} + + public GasOracleBuilder withLastBlock(Long lastBlock) { + this.lastBlock = lastBlock; + return this; + } + + public GasOracleBuilder withSafeGasPrice(Wei safeGasPrice) { + this.safeGasPrice = safeGasPrice; + return this; + } + + public GasOracleBuilder withProposeGasPrice(Wei proposeGasPrice) { + this.proposeGasPrice = proposeGasPrice; + return this; + } + + public GasOracleBuilder withFastGasPrice(Wei fastGasPrice) { + this.fastGasPrice = fastGasPrice; + return this; + } + + public GasOracleBuilder withSuggestBaseFee(BigDecimal suggestBaseFee) { + this.suggestBaseFee = suggestBaseFee; + return this; + } + + public GasOracleBuilder withGasUsedRatio(List gasUsedRatio) { + this.gasUsedRatio = gasUsedRatio; + return this; + } + + public GasOracle build() { + GasOracle gasOracle = new GasOracle(); + gasOracle.LastBlock = this.lastBlock; + gasOracle.suggestBaseFee = this.suggestBaseFee; + if (this.proposeGasPrice != null) { + gasOracle.ProposeGasPrice = this.proposeGasPrice.asGwei().toBigInteger(); + } + if (this.safeGasPrice != null) { + gasOracle.SafeGasPrice = this.safeGasPrice.asGwei().toBigInteger(); + } + if (this.fastGasPrice != null) { + gasOracle.FastGasPrice = this.fastGasPrice.asGwei().toBigInteger(); + } + if (this.gasUsedRatio != null) { + gasOracle.gasUsedRatio = this.gasUsedRatio.stream() + .map(BigDecimal::toString) + .collect(Collectors.joining(",")); + } + return gasOracle; + } + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Log.java b/src/main/java/io/goodforgod/api/etherscan/model/Log.java new file mode 100644 index 0000000..da6c295 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/Log.java @@ -0,0 +1,248 @@ +package io.goodforgod.api.etherscan.model; + +import com.google.gson.annotations.Expose; +import io.goodforgod.api.etherscan.util.BasicUtils; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.List; +import java.util.Objects; + +/** + * @author GoodforGod + * @since 31.10.2018 + */ +public class Log { + + private String blockNumber; + @Expose(deserialize = false, serialize = false) + private Long _blockNumber; + private String address; + private String transactionHash; + private String transactionIndex; + @Expose(deserialize = false, serialize = false) + private Long _transactionIndex; + private String timeStamp; + @Expose(deserialize = false, serialize = false) + private LocalDateTime _timeStamp; + private String data; + private String gasPrice; + @Expose(deserialize = false, serialize = false) + private Wei _gasPrice; + private String gasUsed; + @Expose(deserialize = false, serialize = false) + private Wei _gasUsed; + private List topics; + private String logIndex; + @Expose(deserialize = false, serialize = false) + private Long _logIndex; + + protected Log() {} + + // + public Long getBlockNumber() { + if (_blockNumber == null && !BasicUtils.isEmpty(blockNumber)) { + _blockNumber = BasicUtils.parseHex(blockNumber).longValue(); + } + return _blockNumber; + } + + public String getAddress() { + return address; + } + + public String getTransactionHash() { + return transactionHash; + } + + public Long getTransactionIndex() { + if (_transactionIndex == null && !BasicUtils.isEmpty(transactionIndex)) { + _transactionIndex = BasicUtils.parseHex(transactionIndex).longValue(); + } + + return _transactionIndex; + } + + public LocalDateTime getTimeStamp() { + if (_timeStamp == null && !BasicUtils.isEmpty(timeStamp)) { + long formatted = getTimeStampAsSeconds(); + _timeStamp = LocalDateTime.ofEpochSecond(formatted, 0, ZoneOffset.UTC); + } + return _timeStamp; + } + + /** + * Return the "timeStamp" field of the event record as a long-int representing the seconds + * since the Unix epoch (1970-01-01 00:00:00). + * + * @return milliseconds between Unix epoch and `timeStamp`. If field is empty or null, returns null + */ + public Long getTimeStampAsSeconds() { + if (BasicUtils.isEmpty(timeStamp)) { + return null; + } + + return (timeStamp.charAt(0) == '0' && timeStamp.charAt(1) == 'x') + ? BasicUtils.parseHex(timeStamp).longValue() + : Long.parseLong(timeStamp); + } + + public String getData() { + return data; + } + + public Wei getGasPrice() { + if (!BasicUtils.isEmpty(gasPrice)) { + _gasPrice = Wei.ofWei(BasicUtils.parseHex(gasPrice)); + } + + return _gasPrice; + } + + public Wei getGasUsed() { + if (!BasicUtils.isEmpty(gasUsed)) { + _gasUsed = Wei.ofWei(BasicUtils.parseHex(gasUsed)); + } + + return _gasUsed; + } + + public List getTopics() { + return topics; + } + + public Long getLogIndex() { + if (_logIndex == null && !BasicUtils.isEmpty(logIndex)) { + _logIndex = BasicUtils.parseHex(logIndex).longValue(); + } + return _logIndex; + } + // + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof Log)) + return false; + Log log = (Log) o; + return Objects.equals(blockNumber, log.blockNumber) && Objects.equals(address, log.address) + && Objects.equals(transactionHash, log.transactionHash) && Objects.equals(transactionIndex, log.transactionIndex) + && Objects.equals(logIndex, log.logIndex); + } + + @Override + public int hashCode() { + return Objects.hash(blockNumber, address, transactionHash, transactionIndex, logIndex); + } + + @Override + public String toString() { + return "Log{" + + "blockNumber='" + blockNumber + '\'' + + ", address='" + address + '\'' + + ", transactionHash='" + transactionHash + '\'' + + ", transactionIndex='" + transactionIndex + '\'' + + ", timeStamp='" + timeStamp + '\'' + + ", data='" + data + '\'' + + ", gasPrice='" + gasPrice + '\'' + + ", gasUsed='" + gasUsed + '\'' + + ", topics=" + topics + + ", logIndex='" + logIndex + '\'' + + '}'; + } + + public static LogBuilder builder() { + return new LogBuilder(); + } + + public static final class LogBuilder { + + private Long blockNumber; + private String address; + private String transactionHash; + private Long transactionIndex; + private LocalDateTime timeStamp; + private String data; + private Wei gasPrice; + private Wei gasUsed; + private List topics; + private Long logIndex; + + private LogBuilder() {} + + public LogBuilder withBlockNumber(Long blockNumber) { + this.blockNumber = blockNumber; + return this; + } + + public LogBuilder withAddress(String address) { + this.address = address; + return this; + } + + public LogBuilder withTransactionHash(String transactionHash) { + this.transactionHash = transactionHash; + return this; + } + + public LogBuilder withTransactionIndex(Long transactionIndex) { + this.transactionIndex = transactionIndex; + return this; + } + + public LogBuilder withTimeStamp(LocalDateTime timeStamp) { + this.timeStamp = timeStamp; + return this; + } + + public LogBuilder withData(String data) { + this.data = data; + return this; + } + + public LogBuilder withGasPrice(Wei gasPrice) { + this.gasPrice = gasPrice; + return this; + } + + public LogBuilder withGasUsed(Wei gasUsed) { + this.gasUsed = gasUsed; + return this; + } + + public LogBuilder withTopics(List topics) { + this.topics = topics; + return this; + } + + public LogBuilder withLogIndex(Long logIndex) { + this.logIndex = logIndex; + return this; + } + + public Log build() { + Log log = new Log(); + log.address = this.address; + if (this.gasPrice != null) { + log._gasPrice = this.gasPrice; + } + log._logIndex = this.logIndex; + log._transactionIndex = this.transactionIndex; + log.blockNumber = String.valueOf(this.blockNumber); + log.transactionIndex = String.valueOf(this.transactionIndex); + if (this.timeStamp != null) { + log.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + log._timeStamp = this.timeStamp; + } + log.data = this.data; + if (this.gasUsed != null) { + log._gasUsed = this.gasUsed; + } + log.logIndex = String.valueOf(this.logIndex); + log._blockNumber = this.blockNumber; + log.topics = this.topics; + log.transactionHash = this.transactionHash; + return log; + } + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Price.java b/src/main/java/io/goodforgod/api/etherscan/model/Price.java new file mode 100644 index 0000000..565dbed --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/Price.java @@ -0,0 +1,123 @@ +package io.goodforgod.api.etherscan.model; + +import com.google.gson.annotations.Expose; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.Objects; + +/** + * @author GoodforGod + * @since 30.10.2018 + */ +public class Price { + + private BigDecimal ethusd; + private BigDecimal ethbtc; + private String ethusd_timestamp; + private String ethbtc_timestamp; + @Expose(deserialize = false, serialize = false) + private LocalDateTime _ethusd_timestamp; + @Expose(deserialize = false, serialize = false) + private LocalDateTime _ethbtc_timestamp; + + protected Price() {} + + public BigDecimal inUsd() { + return ethusd; + } + + public BigDecimal inBtc() { + return ethbtc; + } + + public LocalDateTime timestampUsd() { + if (_ethusd_timestamp == null && ethusd_timestamp != null) { + _ethusd_timestamp = LocalDateTime.ofEpochSecond(Long.parseLong(ethusd_timestamp), 0, ZoneOffset.UTC); + } + return _ethusd_timestamp; + } + + public LocalDateTime timestampBtc() { + if (_ethbtc_timestamp == null && ethbtc_timestamp != null) { + _ethbtc_timestamp = LocalDateTime.ofEpochSecond(Long.parseLong(ethbtc_timestamp), 0, ZoneOffset.UTC); + } + return _ethbtc_timestamp; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof Price)) + return false; + Price price = (Price) o; + return Objects.equals(ethusd, price.ethusd) && Objects.equals(ethbtc, price.ethbtc) + && Objects.equals(ethusd_timestamp, price.ethusd_timestamp) + && Objects.equals(ethbtc_timestamp, price.ethbtc_timestamp); + } + + @Override + public int hashCode() { + return Objects.hash(ethusd, ethbtc, ethusd_timestamp, ethbtc_timestamp); + } + + @Override + public String toString() { + return "Price{" + + "ethusd=" + ethusd + + ", ethbtc=" + ethbtc + + ", ethusd_timestamp='" + ethusd_timestamp + '\'' + + ", ethbtc_timestamp='" + ethbtc_timestamp + '\'' + + '}'; + } + + public static PriceBuilder builder() { + return new PriceBuilder(); + } + + public static final class PriceBuilder { + + private BigDecimal ethusd; + private BigDecimal ethbtc; + private LocalDateTime ethusdTimestamp; + private LocalDateTime ethbtcTimestamp; + + private PriceBuilder() {} + + public PriceBuilder withUsd(BigDecimal ethToUsd) { + this.ethusd = ethToUsd; + return this; + } + + public PriceBuilder withBtc(BigDecimal ethToBtc) { + this.ethbtc = ethToBtc; + return this; + } + + public PriceBuilder withTimestampUsd(LocalDateTime ethToUsdTimestamp) { + this.ethusdTimestamp = ethToUsdTimestamp; + return this; + } + + public PriceBuilder withTimestampBtc(LocalDateTime ethToBtcTimestamp) { + this.ethbtcTimestamp = ethToBtcTimestamp; + return this; + } + + public Price build() { + Price price = new Price(); + price.ethbtc = this.ethbtc; + price.ethusd = this.ethusd; + if (this.ethbtcTimestamp != null) { + price.ethbtc_timestamp = String.valueOf(this.ethbtcTimestamp.toEpochSecond(ZoneOffset.UTC)); + price._ethbtc_timestamp = this.ethbtcTimestamp; + } + if (this.ethusdTimestamp != null) { + price.ethusd_timestamp = String.valueOf(this.ethusdTimestamp.toEpochSecond(ZoneOffset.UTC)); + price._ethusd_timestamp = this.ethusdTimestamp; + } + return price; + } + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Status.java b/src/main/java/io/goodforgod/api/etherscan/model/Status.java new file mode 100644 index 0000000..052c187 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/Status.java @@ -0,0 +1,80 @@ +package io.goodforgod.api.etherscan.model; + +import java.util.Objects; + +/** + * Contract Execution Status + * + * @author GoodforGod + * @since 30.10.2018 + */ +public class Status { + + /** + * "0" = Pass , isError":"1" = Error during Contract Execution + */ + private int isError; + private String errDescription; + + protected Status() {} + + public boolean haveError() { + return isError == 1; + } + + public String getErrDescription() { + return errDescription; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof Status)) + return false; + Status status = (Status) o; + return isError == status.isError && Objects.equals(errDescription, status.errDescription); + } + + @Override + public int hashCode() { + return Objects.hash(isError, errDescription); + } + + @Override + public String toString() { + return "Status{" + + "isError=" + isError + + ", errDescription='" + errDescription + '\'' + + '}'; + } + + public static StatusBuilder builder() { + return new StatusBuilder(); + } + + public static final class StatusBuilder { + + private int isError; + private String errDescription; + + private StatusBuilder() {} + + public StatusBuilder withIsError(int isError) { + this.isError = isError; + return this; + } + + public StatusBuilder withErrDescription(String errDescription) { + this.errDescription = errDescription; + return this; + } + + public Status build() { + Status status = new Status(); + status.isError = this.isError; + status.errDescription = this.errDescription; + return status; + } + } +} diff --git a/src/main/java/io/api/etherscan/model/TokenBalance.java b/src/main/java/io/goodforgod/api/etherscan/model/TokenBalance.java similarity index 69% rename from src/main/java/io/api/etherscan/model/TokenBalance.java rename to src/main/java/io/goodforgod/api/etherscan/model/TokenBalance.java index d057992..bb40ee2 100644 --- a/src/main/java/io/api/etherscan/model/TokenBalance.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TokenBalance.java @@ -1,11 +1,8 @@ -package io.api.etherscan.model; +package io.goodforgod.api.etherscan.model; -import java.math.BigInteger; import java.util.Objects; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 31.10.2018 */ @@ -13,7 +10,7 @@ public class TokenBalance extends Balance { private final String tokenContract; - public TokenBalance(String address, BigInteger balance, String tokenContract) { + public TokenBalance(String address, Wei balance, String tokenContract) { super(address, balance); this.tokenContract = tokenContract; } @@ -26,20 +23,17 @@ public String getContract() { public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) + if (!(o instanceof TokenBalance)) return false; if (!super.equals(o)) return false; - TokenBalance that = (TokenBalance) o; return Objects.equals(tokenContract, that.tokenContract); } @Override public int hashCode() { - int result = super.hashCode(); - result = 31 * result + (tokenContract != null ? tokenContract.hashCode() : 0); - return result; + return Objects.hash(super.hashCode(), tokenContract); } @Override diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Tx.java b/src/main/java/io/goodforgod/api/etherscan/model/Tx.java new file mode 100644 index 0000000..7ef0e22 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/Tx.java @@ -0,0 +1,209 @@ +package io.goodforgod.api.etherscan.model; + +import io.goodforgod.api.etherscan.util.BasicUtils; +import java.math.BigInteger; +import java.time.LocalDateTime; +import java.time.ZoneOffset; + +/** + * @author GoodforGod + * @since 28.10.2018 + */ +public class Tx extends BlockTx { + + private BigInteger value; + private String isError; + private String txreceipt_status; + + protected Tx() {} + + // + public BigInteger getValue() { + return value; + } + + public boolean haveError() { + return !BasicUtils.isEmpty(isError) && !isError.equals("0"); + } + + public String getTxReceiptStatus() { + return txreceipt_status; + } + // + + @Override + public String toString() { + return "Tx{" + + "value=" + value + + ", isError='" + isError + '\'' + + ", txreceipt_status='" + txreceipt_status + '\'' + + ", nonce=" + nonce + + ", blockHash='" + blockHash + '\'' + + ", transactionIndex=" + transactionIndex + + ", confirmations=" + confirmations + + ", gasPrice=" + gasPrice + + ", cumulativeGasUsed=" + cumulativeGasUsed + + ", blockNumber=" + blockNumber + + ", timeStamp='" + timeStamp + '\'' + + ", hash='" + hash + '\'' + + ", from='" + from + '\'' + + ", to='" + to + '\'' + + ", contractAddress='" + contractAddress + '\'' + + ", input='" + input + '\'' + + ", gas=" + gas + + ", gasUsed=" + gasUsed + + '}'; + } + + public static TxBuilder builder() { + return new TxBuilder(); + } + + public static final class TxBuilder { + + private long blockNumber; + private LocalDateTime timeStamp; + private String hash; + private String from; + private String to; + private BigInteger value; + private String contractAddress; + private String input; + private Wei gas; + private Wei gasUsed; + private long nonce; + private String blockHash; + private int transactionIndex; + private Wei gasPrice; + private Wei cumulativeGasUsed; + private long confirmations; + private String isError; + private String txReceiptStatus; + + private TxBuilder() {} + + public TxBuilder withBlockNumber(long blockNumber) { + this.blockNumber = blockNumber; + return this; + } + + public TxBuilder withTimeStamp(LocalDateTime timeStamp) { + this.timeStamp = timeStamp; + return this; + } + + public TxBuilder withHash(String hash) { + this.hash = hash; + return this; + } + + public TxBuilder withFrom(String from) { + this.from = from; + return this; + } + + public TxBuilder withTo(String to) { + this.to = to; + return this; + } + + public TxBuilder withValue(BigInteger value) { + this.value = value; + return this; + } + + public TxBuilder withContractAddress(String contractAddress) { + this.contractAddress = contractAddress; + return this; + } + + public TxBuilder withInput(String input) { + this.input = input; + return this; + } + + public TxBuilder withGas(Wei gas) { + this.gas = gas; + return this; + } + + public TxBuilder withGasUsed(Wei gasUsed) { + this.gasUsed = gasUsed; + return this; + } + + public TxBuilder withNonce(long nonce) { + this.nonce = nonce; + return this; + } + + public TxBuilder withBlockHash(String blockHash) { + this.blockHash = blockHash; + return this; + } + + public TxBuilder withTransactionIndex(int transactionIndex) { + this.transactionIndex = transactionIndex; + return this; + } + + public TxBuilder withGasPrice(Wei gasPrice) { + this.gasPrice = gasPrice; + return this; + } + + public TxBuilder withCumulativeGasUsed(Wei cumulativeGasUsed) { + this.cumulativeGasUsed = cumulativeGasUsed; + return this; + } + + public TxBuilder withConfirmations(long confirmations) { + this.confirmations = confirmations; + return this; + } + + public TxBuilder withIsError(String isError) { + this.isError = isError; + return this; + } + + public TxBuilder withTxReceiptStatus(String txReceiptStatus) { + this.txReceiptStatus = txReceiptStatus; + return this; + } + + public Tx build() { + Tx tx = new Tx(); + tx.isError = this.isError; + tx.blockHash = this.blockHash; + tx.hash = this.hash; + if (this.gas != null) { + tx.gas = this.gas.asWei(); + } + if (this.gasUsed != null) { + tx.gasUsed = this.gasUsed.asWei(); + } + if (this.gasPrice != null) { + tx.gasPrice = this.gasPrice.asWei(); + } + if (this.cumulativeGasUsed != null) { + tx.cumulativeGasUsed = this.cumulativeGasUsed.asWei(); + } + tx.from = this.from; + tx.txreceipt_status = this.txReceiptStatus; + tx.contractAddress = this.contractAddress; + tx.value = this.value; + tx.transactionIndex = this.transactionIndex; + tx.confirmations = this.confirmations; + if (this.timeStamp != null) { + tx.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + tx._timeStamp = this.timeStamp; + } + tx.nonce = this.nonce; + tx.blockNumber = this.blockNumber; + tx.to = this.to; + tx.input = this.input; + return tx; + } + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java b/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java new file mode 100644 index 0000000..16d4457 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java @@ -0,0 +1,239 @@ +package io.goodforgod.api.etherscan.model; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.Objects; + +/** + * @author GoodforGod + * @since 28.10.2018 + */ +public class TxErc1155 extends BlockTx { + + private String tokenID; + private String tokenName; + private String tokenSymbol; + private String tokenValue; + + protected TxErc1155() {} + + // + public String getTokenID() { + return tokenID; + } + + public String getTokenName() { + return tokenName; + } + + public String getTokenSymbol() { + return tokenSymbol; + } + + public String getTokenValue() { + return tokenValue; + } + // + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof TxErc1155)) + return false; + if (!super.equals(o)) + return false; + TxErc1155 txErc1155 = (TxErc1155) o; + return Objects.equals(tokenID, txErc1155.tokenID) && Objects.equals(tokenName, txErc1155.tokenName) + && Objects.equals(tokenSymbol, txErc1155.tokenSymbol) && Objects.equals(tokenValue, txErc1155.tokenValue); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), tokenID, tokenName, tokenSymbol, tokenValue); + } + + @Override + public String toString() { + return "TxErc1155{" + + "tokenID='" + tokenID + '\'' + + ", tokenName='" + tokenName + '\'' + + ", tokenSymbol='" + tokenSymbol + '\'' + + ", tokenValue='" + tokenValue + '\'' + + ", nonce=" + nonce + + ", blockHash='" + blockHash + '\'' + + ", transactionIndex=" + transactionIndex + + ", confirmations=" + confirmations + + ", gasPrice=" + gasPrice + + ", cumulativeGasUsed=" + cumulativeGasUsed + + ", blockNumber=" + blockNumber + + ", timeStamp='" + timeStamp + '\'' + + ", hash='" + hash + '\'' + + ", from='" + from + '\'' + + ", to='" + to + '\'' + + ", contractAddress='" + contractAddress + '\'' + + ", input='" + input + '\'' + + ", gas=" + gas + + ", gasUsed=" + gasUsed + + '}'; + } + + public static TxErc1155Builder builder() { + return new TxErc1155Builder(); + } + + public static final class TxErc1155Builder { + + private long blockNumber; + private LocalDateTime timeStamp; + private String hash; + private String from; + private String to; + private String contractAddress; + private String input; + private long nonce; + private String blockHash; + private String tokenID; + private String tokenName; + private String tokenSymbol; + private String tokenValue; + private int transactionIndex; + private Wei gas; + private Wei gasUsed; + private Wei gasPrice; + private Wei cumulativeGasUsed; + private long confirmations; + + private TxErc1155Builder() {} + + public TxErc1155Builder withBlockNumber(long blockNumber) { + this.blockNumber = blockNumber; + return this; + } + + public TxErc1155Builder withTimeStamp(LocalDateTime timeStamp) { + this.timeStamp = timeStamp; + return this; + } + + public TxErc1155Builder withHash(String hash) { + this.hash = hash; + return this; + } + + public TxErc1155Builder withFrom(String from) { + this.from = from; + return this; + } + + public TxErc1155Builder withTo(String to) { + this.to = to; + return this; + } + + public TxErc1155Builder withContractAddress(String contractAddress) { + this.contractAddress = contractAddress; + return this; + } + + public TxErc1155Builder withInput(String input) { + this.input = input; + return this; + } + + public TxErc1155Builder withGas(Wei gas) { + this.gas = gas; + return this; + } + + public TxErc1155Builder withGasUsed(Wei gasUsed) { + this.gasUsed = gasUsed; + return this; + } + + public TxErc1155Builder withNonce(long nonce) { + this.nonce = nonce; + return this; + } + + public TxErc1155Builder withBlockHash(String blockHash) { + this.blockHash = blockHash; + return this; + } + + public TxErc1155Builder withTokenID(String tokenID) { + this.tokenID = tokenID; + return this; + } + + public TxErc1155Builder withTokenName(String tokenName) { + this.tokenName = tokenName; + return this; + } + + public TxErc1155Builder withTokenSymbol(String tokenSymbol) { + this.tokenSymbol = tokenSymbol; + return this; + } + + public TxErc1155Builder withTokenDecimal(String tokenDecimal) { + this.tokenValue = tokenDecimal; + return this; + } + + public TxErc1155Builder withTransactionIndex(int transactionIndex) { + this.transactionIndex = transactionIndex; + return this; + } + + public TxErc1155Builder withGasPrice(Wei gasPrice) { + this.gasPrice = gasPrice; + return this; + } + + public TxErc1155Builder withCumulativeGasUsed(Wei cumulativeGasUsed) { + this.cumulativeGasUsed = cumulativeGasUsed; + return this; + } + + public TxErc1155Builder withConfirmations(long confirmations) { + this.confirmations = confirmations; + return this; + } + + public TxErc1155 build() { + TxErc1155 txERC1155 = new TxErc1155(); + txERC1155.tokenName = this.tokenName; + txERC1155.hash = this.hash; + txERC1155.nonce = this.nonce; + txERC1155.from = this.from; + if (this.gas != null) { + txERC1155.gas = this.gas.asWei(); + } + if (this.gasUsed != null) { + txERC1155.gasUsed = this.gasUsed.asWei(); + } + if (this.gasPrice != null) { + txERC1155.gasPrice = this.gasPrice.asWei(); + } + if (this.cumulativeGasUsed != null) { + txERC1155.cumulativeGasUsed = this.cumulativeGasUsed.asWei(); + } + txERC1155.contractAddress = this.contractAddress; + txERC1155.tokenID = this.tokenID; + if (this.timeStamp != null) { + txERC1155.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + txERC1155._timeStamp = this.timeStamp; + } + txERC1155.blockNumber = this.blockNumber; + txERC1155.tokenValue = this.tokenValue; + txERC1155.transactionIndex = this.transactionIndex; + txERC1155.to = this.to; + txERC1155.confirmations = this.confirmations; + txERC1155.input = this.input; + txERC1155.blockHash = this.blockHash; + txERC1155.tokenSymbol = this.tokenSymbol; + return txERC1155; + } + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java b/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java new file mode 100644 index 0000000..3dc22fd --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java @@ -0,0 +1,240 @@ +package io.goodforgod.api.etherscan.model; + +import java.math.BigInteger; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.Objects; + +/** + * @author GoodforGod + * @since 28.10.2018 + */ +public class TxErc20 extends BlockTx { + + private BigInteger value; + private String tokenName; + private String tokenSymbol; + private String tokenDecimal; + + protected TxErc20() {} + + // + public BigInteger getValue() { + return value; + } + + public String getTokenName() { + return tokenName; + } + + public String getTokenSymbol() { + return tokenSymbol; + } + + public String getTokenDecimal() { + return tokenDecimal; + } + // + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof TxErc20)) + return false; + if (!super.equals(o)) + return false; + TxErc20 txErc20 = (TxErc20) o; + return Objects.equals(tokenName, txErc20.tokenName) && Objects.equals(tokenSymbol, txErc20.tokenSymbol) + && Objects.equals(tokenDecimal, txErc20.tokenDecimal); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), tokenName, tokenSymbol, tokenDecimal); + } + + @Override + public String toString() { + return "TxErc20{" + + "value=" + value + + ", tokenName='" + tokenName + '\'' + + ", tokenSymbol='" + tokenSymbol + '\'' + + ", tokenDecimal='" + tokenDecimal + '\'' + + ", nonce=" + nonce + + ", blockHash='" + blockHash + '\'' + + ", transactionIndex=" + transactionIndex + + ", confirmations=" + confirmations + + ", gasPrice=" + gasPrice + + ", cumulativeGasUsed=" + cumulativeGasUsed + + ", blockNumber=" + blockNumber + + ", timeStamp='" + timeStamp + '\'' + + ", hash='" + hash + '\'' + + ", from='" + from + '\'' + + ", to='" + to + '\'' + + ", contractAddress='" + contractAddress + '\'' + + ", input='" + input + '\'' + + ", gas=" + gas + + ", gasUsed=" + gasUsed + + '}'; + } + + public static TxERC20Builder builder() { + return new TxERC20Builder(); + } + + public static final class TxERC20Builder { + + private long blockNumber; + private LocalDateTime timeStamp; + private String hash; + private String from; + private String to; + private BigInteger value; + private String contractAddress; + private String input; + private Wei gas; + private Wei gasUsed; + private long nonce; + private String blockHash; + private String tokenName; + private String tokenSymbol; + private String tokenDecimal; + private int transactionIndex; + private Wei gasPrice; + private Wei cumulativeGasUsed; + private long confirmations; + + private TxERC20Builder() {} + + public TxERC20Builder withBlockNumber(long blockNumber) { + this.blockNumber = blockNumber; + return this; + } + + public TxERC20Builder withTimeStamp(LocalDateTime timeStamp) { + this.timeStamp = timeStamp; + return this; + } + + public TxERC20Builder withHash(String hash) { + this.hash = hash; + return this; + } + + public TxERC20Builder withFrom(String from) { + this.from = from; + return this; + } + + public TxERC20Builder withTo(String to) { + this.to = to; + return this; + } + + public TxERC20Builder withValue(BigInteger value) { + this.value = value; + return this; + } + + public TxERC20Builder withContractAddress(String contractAddress) { + this.contractAddress = contractAddress; + return this; + } + + public TxERC20Builder withInput(String input) { + this.input = input; + return this; + } + + public TxERC20Builder withGas(Wei gas) { + this.gas = gas; + return this; + } + + public TxERC20Builder withGasUsed(Wei gasUsed) { + this.gasUsed = gasUsed; + return this; + } + + public TxERC20Builder withNonce(long nonce) { + this.nonce = nonce; + return this; + } + + public TxERC20Builder withBlockHash(String blockHash) { + this.blockHash = blockHash; + return this; + } + + public TxERC20Builder withTokenName(String tokenName) { + this.tokenName = tokenName; + return this; + } + + public TxERC20Builder withTokenSymbol(String tokenSymbol) { + this.tokenSymbol = tokenSymbol; + return this; + } + + public TxERC20Builder withTokenDecimal(String tokenDecimal) { + this.tokenDecimal = tokenDecimal; + return this; + } + + public TxERC20Builder withTransactionIndex(int transactionIndex) { + this.transactionIndex = transactionIndex; + return this; + } + + public TxERC20Builder withGasPrice(Wei gasPrice) { + this.gasPrice = gasPrice; + return this; + } + + public TxERC20Builder withCumulativeGasUsed(Wei cumulativeGasUsed) { + this.cumulativeGasUsed = cumulativeGasUsed; + return this; + } + + public TxERC20Builder withConfirmations(long confirmations) { + this.confirmations = confirmations; + return this; + } + + public TxErc20 build() { + TxErc20 txERC20 = new TxErc20(); + txERC20.tokenName = this.tokenName; + txERC20.hash = this.hash; + if (this.gas != null) { + txERC20.gas = this.gas.asWei(); + } + if (this.gasUsed != null) { + txERC20.gasUsed = this.gasUsed.asWei(); + } + if (this.gasPrice != null) { + txERC20.gasPrice = this.gasPrice.asWei(); + } + if (this.cumulativeGasUsed != null) { + txERC20.cumulativeGasUsed = this.cumulativeGasUsed.asWei(); + } + txERC20.from = this.from; + txERC20.tokenSymbol = this.tokenSymbol; + txERC20.transactionIndex = this.transactionIndex; + txERC20.contractAddress = this.contractAddress; + txERC20.nonce = this.nonce; + txERC20.confirmations = this.confirmations; + txERC20.value = this.value; + if (this.timeStamp != null) { + txERC20.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + txERC20._timeStamp = this.timeStamp; + } + txERC20.blockHash = this.blockHash; + txERC20.blockNumber = this.blockNumber; + txERC20.to = this.to; + txERC20.input = this.input; + txERC20.tokenDecimal = this.tokenDecimal; + return txERC20; + } + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java b/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java new file mode 100644 index 0000000..2180019 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java @@ -0,0 +1,239 @@ +package io.goodforgod.api.etherscan.model; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.Objects; + +/** + * @author GoodforGod + * @since 28.10.2018 + */ +public class TxErc721 extends BlockTx { + + private String tokenID; + private String tokenName; + private String tokenSymbol; + private String tokenDecimal; + + protected TxErc721() {} + + // + public String getTokenID() { + return tokenID; + } + + public String getTokenName() { + return tokenName; + } + + public String getTokenSymbol() { + return tokenSymbol; + } + + public String getTokenDecimal() { + return tokenDecimal; + } + // + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof TxErc721)) + return false; + if (!super.equals(o)) + return false; + TxErc721 txErc721 = (TxErc721) o; + return Objects.equals(tokenID, txErc721.tokenID) && Objects.equals(tokenName, txErc721.tokenName) + && Objects.equals(tokenSymbol, txErc721.tokenSymbol) && Objects.equals(tokenDecimal, txErc721.tokenDecimal); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), tokenID, tokenName, tokenSymbol, tokenDecimal); + } + + @Override + public String toString() { + return "TxErc721{" + + "tokenID='" + tokenID + '\'' + + ", tokenName='" + tokenName + '\'' + + ", tokenSymbol='" + tokenSymbol + '\'' + + ", tokenDecimal='" + tokenDecimal + '\'' + + ", nonce=" + nonce + + ", blockHash='" + blockHash + '\'' + + ", transactionIndex=" + transactionIndex + + ", confirmations=" + confirmations + + ", gasPrice=" + gasPrice + + ", cumulativeGasUsed=" + cumulativeGasUsed + + ", blockNumber=" + blockNumber + + ", timeStamp='" + timeStamp + '\'' + + ", hash='" + hash + '\'' + + ", from='" + from + '\'' + + ", to='" + to + '\'' + + ", contractAddress='" + contractAddress + '\'' + + ", input='" + input + '\'' + + ", gas=" + gas + + ", gasUsed=" + gasUsed + + '}'; + } + + public static TxERC721Builder builder() { + return new TxERC721Builder(); + } + + public static final class TxERC721Builder { + + private long blockNumber; + private LocalDateTime timeStamp; + private String hash; + private String from; + private String to; + private String contractAddress; + private String input; + private long nonce; + private String blockHash; + private String tokenID; + private String tokenName; + private String tokenSymbol; + private String tokenDecimal; + private int transactionIndex; + private Wei gas; + private Wei gasUsed; + private Wei gasPrice; + private Wei cumulativeGasUsed; + private long confirmations; + + private TxERC721Builder() {} + + public TxERC721Builder withBlockNumber(long blockNumber) { + this.blockNumber = blockNumber; + return this; + } + + public TxERC721Builder withTimeStamp(LocalDateTime timeStamp) { + this.timeStamp = timeStamp; + return this; + } + + public TxERC721Builder withHash(String hash) { + this.hash = hash; + return this; + } + + public TxERC721Builder withFrom(String from) { + this.from = from; + return this; + } + + public TxERC721Builder withTo(String to) { + this.to = to; + return this; + } + + public TxERC721Builder withContractAddress(String contractAddress) { + this.contractAddress = contractAddress; + return this; + } + + public TxERC721Builder withInput(String input) { + this.input = input; + return this; + } + + public TxERC721Builder withGas(Wei gas) { + this.gas = gas; + return this; + } + + public TxERC721Builder withGasUsed(Wei gasUsed) { + this.gasUsed = gasUsed; + return this; + } + + public TxERC721Builder withNonce(long nonce) { + this.nonce = nonce; + return this; + } + + public TxERC721Builder withBlockHash(String blockHash) { + this.blockHash = blockHash; + return this; + } + + public TxERC721Builder withTokenID(String tokenID) { + this.tokenID = tokenID; + return this; + } + + public TxERC721Builder withTokenName(String tokenName) { + this.tokenName = tokenName; + return this; + } + + public TxERC721Builder withTokenSymbol(String tokenSymbol) { + this.tokenSymbol = tokenSymbol; + return this; + } + + public TxERC721Builder withTokenDecimal(String tokenDecimal) { + this.tokenDecimal = tokenDecimal; + return this; + } + + public TxERC721Builder withTransactionIndex(int transactionIndex) { + this.transactionIndex = transactionIndex; + return this; + } + + public TxERC721Builder withGasPrice(Wei gasPrice) { + this.gasPrice = gasPrice; + return this; + } + + public TxERC721Builder withCumulativeGasUsed(Wei cumulativeGasUsed) { + this.cumulativeGasUsed = cumulativeGasUsed; + return this; + } + + public TxERC721Builder withConfirmations(long confirmations) { + this.confirmations = confirmations; + return this; + } + + public TxErc721 build() { + TxErc721 txERC721 = new TxErc721(); + txERC721.tokenName = this.tokenName; + txERC721.hash = this.hash; + txERC721.nonce = this.nonce; + txERC721.from = this.from; + if (this.gas != null) { + txERC721.gas = this.gas.asWei(); + } + if (this.gasUsed != null) { + txERC721.gasUsed = this.gasUsed.asWei(); + } + if (this.gasPrice != null) { + txERC721.gasPrice = this.gasPrice.asWei(); + } + if (this.cumulativeGasUsed != null) { + txERC721.cumulativeGasUsed = this.cumulativeGasUsed.asWei(); + } + txERC721.contractAddress = this.contractAddress; + txERC721.tokenID = this.tokenID; + if (this.timeStamp != null) { + txERC721.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + txERC721._timeStamp = this.timeStamp; + } + txERC721.blockNumber = this.blockNumber; + txERC721.tokenDecimal = this.tokenDecimal; + txERC721.transactionIndex = this.transactionIndex; + txERC721.to = this.to; + txERC721.confirmations = this.confirmations; + txERC721.input = this.input; + txERC721.blockHash = this.blockHash; + txERC721.tokenSymbol = this.tokenSymbol; + return txERC721; + } + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java b/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java new file mode 100644 index 0000000..a61cf83 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java @@ -0,0 +1,207 @@ +package io.goodforgod.api.etherscan.model; + +import java.math.BigInteger; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.Objects; + +/** + * @author GoodforGod + * @since 29.10.2018 + */ +public class TxInternal extends BaseTx { + + private BigInteger value; + private String type; + private String traceId; + private int isError; + private String errCode; + + protected TxInternal() {} + + // + public BigInteger getValue() { + return value; + } + + public String getType() { + return type; + } + + public long getTraceId() { + return (traceId == null) + ? 0 + : Long.parseLong(traceId); + } + + public String getTraceIdAsString() { + return traceId; + } + + public boolean haveError() { + return isError == 1; + } + + public String getErrCode() { + return errCode; + } + // + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof TxInternal)) + return false; + if (!super.equals(o)) + return false; + TxInternal that = (TxInternal) o; + return isError == that.isError && Objects.equals(value, that.value) && Objects.equals(type, that.type) + && Objects.equals(traceId, that.traceId) && Objects.equals(errCode, that.errCode); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), value, type, traceId, isError, errCode); + } + + @Override + public String toString() { + return "TxInternal{" + + "value=" + value + + ", type='" + type + '\'' + + ", traceId='" + traceId + '\'' + + ", isError=" + isError + + ", errCode='" + errCode + '\'' + + ", blockNumber=" + blockNumber + + ", timeStamp='" + timeStamp + '\'' + + ", hash='" + hash + '\'' + + ", from='" + from + '\'' + + ", to='" + to + '\'' + + ", contractAddress='" + contractAddress + '\'' + + ", input='" + input + '\'' + + ", gas=" + gas + + ", gasUsed=" + gasUsed + + '}'; + } + + public static TxInternalBuilder builder() { + return new TxInternalBuilder(); + } + + public static final class TxInternalBuilder { + + private long blockNumber; + private LocalDateTime timeStamp; + private String hash; + private String from; + private String to; + private BigInteger value; + private String contractAddress; + private String input; + private Wei gas; + private Wei gasUsed; + private String type; + private String traceId; + private int isError; + private String errCode; + + private TxInternalBuilder() {} + + public TxInternalBuilder withBlockNumber(long blockNumber) { + this.blockNumber = blockNumber; + return this; + } + + public TxInternalBuilder withTimeStamp(LocalDateTime timeStamp) { + this.timeStamp = timeStamp; + return this; + } + + public TxInternalBuilder withHash(String hash) { + this.hash = hash; + return this; + } + + public TxInternalBuilder withFrom(String from) { + this.from = from; + return this; + } + + public TxInternalBuilder withTo(String to) { + this.to = to; + return this; + } + + public TxInternalBuilder withValue(BigInteger value) { + this.value = value; + return this; + } + + public TxInternalBuilder withContractAddress(String contractAddress) { + this.contractAddress = contractAddress; + return this; + } + + public TxInternalBuilder withInput(String input) { + this.input = input; + return this; + } + + public TxInternalBuilder withGas(Wei gas) { + this.gas = gas; + return this; + } + + public TxInternalBuilder withGasUsed(Wei gasUsed) { + this.gasUsed = gasUsed; + return this; + } + + public TxInternalBuilder withType(String type) { + this.type = type; + return this; + } + + public TxInternalBuilder withTraceId(String traceId) { + this.traceId = traceId; + return this; + } + + public TxInternalBuilder withIsError(int isError) { + this.isError = isError; + return this; + } + + public TxInternalBuilder withErrCode(String errCode) { + this.errCode = errCode; + return this; + } + + public TxInternal build() { + TxInternal txInternal = new TxInternal(); + txInternal.hash = this.hash; + if (this.gas != null) { + txInternal.gas = this.gas.asWei(); + } + if (this.gasUsed != null) { + txInternal.gasUsed = this.gasUsed.asWei(); + } + txInternal.traceId = this.traceId; + txInternal.type = this.type; + txInternal.from = this.from; + txInternal.contractAddress = this.contractAddress; + txInternal.value = this.value; + if (this.timeStamp != null) { + txInternal.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + txInternal._timeStamp = this.timeStamp; + } + txInternal.errCode = this.errCode; + txInternal.blockNumber = this.blockNumber; + txInternal.isError = this.isError; + txInternal.to = this.to; + txInternal.input = this.input; + return txInternal; + } + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Wei.java b/src/main/java/io/goodforgod/api/etherscan/model/Wei.java new file mode 100644 index 0000000..3180478 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/Wei.java @@ -0,0 +1,146 @@ +package io.goodforgod.api.etherscan.model; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.RoundingMode; +import java.util.Objects; +import org.jetbrains.annotations.NotNull; + +/** + * @author GoodforGod + * @since 30.10.2018 + */ +public class Wei implements Comparable { + + private static final BigDecimal KWEI_POW = BigDecimal.ONE.pow(3); + private static final BigDecimal MWEI_POW = BigDecimal.ONE.pow(6); + private static final BigDecimal GWEI_POW = BigDecimal.ONE.pow(9); + private static final BigDecimal WEI_POW = BigDecimal.ONE.pow(18); + + private final BigInteger result; + + private Wei(BigInteger value) { + this.result = value; + } + + public static Wei ofWei(int value) { + return ofWei(BigInteger.valueOf(value)); + } + + public static Wei ofWei(long value) { + return ofWei(BigInteger.valueOf(value)); + } + + public static Wei ofWei(BigInteger value) { + return new Wei(value); + } + + public static Wei ofKwei(int value) { + return ofKwei(BigInteger.valueOf(value)); + } + + public static Wei ofKwei(long value) { + return ofKwei(BigInteger.valueOf(value)); + } + + public static Wei ofKwei(BigDecimal value) { + return new Wei(value.multiply(KWEI_POW).toBigInteger()); + } + + public static Wei ofKwei(BigInteger value) { + return new Wei(value.multiply(KWEI_POW.toBigInteger())); + } + + public static Wei ofMwei(int value) { + return ofMwei(BigInteger.valueOf(value)); + } + + public static Wei ofMwei(long value) { + return ofMwei(BigInteger.valueOf(value)); + } + + public static Wei ofMwei(BigDecimal value) { + return new Wei(value.multiply(MWEI_POW).toBigInteger()); + } + + public static Wei ofMwei(BigInteger value) { + return new Wei(value.multiply(MWEI_POW.toBigInteger())); + } + + public static Wei ofGwei(int value) { + return ofGwei(BigInteger.valueOf(value)); + } + + public static Wei ofGwei(long value) { + return ofGwei(BigInteger.valueOf(value)); + } + + public static Wei ofGwei(BigDecimal value) { + return new Wei(value.multiply(GWEI_POW).toBigInteger()); + } + + public static Wei ofGwei(BigInteger value) { + return new Wei(value.multiply(GWEI_POW.toBigInteger())); + } + + public static Wei ofEther(int value) { + return ofEther(BigInteger.valueOf(value)); + } + + public static Wei ofEther(long value) { + return ofEther(BigInteger.valueOf(value)); + } + + public static Wei ofEther(BigDecimal value) { + return new Wei(value.multiply(WEI_POW).toBigInteger()); + } + + public static Wei ofEther(BigInteger value) { + return new Wei(value.multiply(WEI_POW.toBigInteger())); + } + + public BigInteger asWei() { + return result; + } + + public BigDecimal asKwei() { + return new BigDecimal(result).divide(KWEI_POW, RoundingMode.HALF_UP); + } + + public BigDecimal asMwei() { + return new BigDecimal(result).divide(MWEI_POW, RoundingMode.HALF_UP); + } + + public BigDecimal asGwei() { + return new BigDecimal(result).divide(GWEI_POW, RoundingMode.HALF_UP); + } + + public BigDecimal asEther() { + return new BigDecimal(result).divide(WEI_POW, RoundingMode.HALF_UP); + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof Wei)) + return false; + Wei wei = (Wei) o; + return Objects.equals(result, wei.result); + } + + @Override + public int hashCode() { + return Objects.hash(result); + } + + @Override + public int compareTo(@NotNull Wei o) { + return result.compareTo(o.result); + } + + @Override + public String toString() { + return result.toString(); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java new file mode 100644 index 0000000..4a2b624 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java @@ -0,0 +1,356 @@ +package io.goodforgod.api.etherscan.model.proxy; + +import com.google.gson.annotations.Expose; +import io.goodforgod.api.etherscan.model.Wei; +import io.goodforgod.api.etherscan.util.BasicUtils; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.List; +import java.util.Objects; +import org.jetbrains.annotations.NotNull; + +/** + * @author GoodforGod + * @since 31.10.2018 + */ +public class BlockProxy implements Comparable { + + private String number; + @Expose(deserialize = false, serialize = false) + private Long _number; + private String hash; + private String parentHash; + private String stateRoot; + private String size; + @Expose(deserialize = false, serialize = false) + private Long _size; + private String difficulty; + private String totalDifficulty; + private String timestamp; + @Expose(deserialize = false, serialize = false) + private LocalDateTime _timestamp; + + private String miner; + private String nonce; + private String extraData; + private String logsBloom; + private String mixHash; + private String gasUsed; + @Expose(deserialize = false, serialize = false) + private Wei _gasUsed; + private String gasLimit; + @Expose(deserialize = false, serialize = false) + private Wei _gasLimit; + + private String sha3Uncles; + private List uncles; + + private String receiptsRoot; + private String transactionsRoot; + private List transactions; + + protected BlockProxy() {} + + // + public Long getNumber() { + if (_number == null && !BasicUtils.isEmpty(number)) + _number = BasicUtils.parseHex(number).longValue(); + return _number; + } + + public String getHash() { + return hash; + } + + public String getParentHash() { + return parentHash; + } + + public String getStateRoot() { + return stateRoot; + } + + public Long getSize() { + if (_size == null && !BasicUtils.isEmpty(size)) + _size = BasicUtils.parseHex(size).longValue(); + return _size; + } + + public String getDifficulty() { + return difficulty; + } + + public String getTotalDifficulty() { + return totalDifficulty; + } + + public LocalDateTime getTimeStamp() { + if (_timestamp == null && !BasicUtils.isEmpty(timestamp)) + _timestamp = LocalDateTime.ofEpochSecond(BasicUtils.parseHex(timestamp).longValue(), 0, ZoneOffset.UTC); + return _timestamp; + } + + public String getMiner() { + return miner; + } + + public String getNonce() { + return nonce; + } + + public String getExtraData() { + return extraData; + } + + public String getLogsBloom() { + return logsBloom; + } + + public String getMixHash() { + return mixHash; + } + + public Wei getGasUsed() { + if (_gasUsed == null && !BasicUtils.isEmpty(gasUsed)) + _gasUsed = Wei.ofWei(BasicUtils.parseHex(gasUsed)); + return _gasUsed; + } + + public Wei getGasLimit() { + if (_gasLimit == null && !BasicUtils.isEmpty(gasLimit)) + _gasLimit = Wei.ofWei(BasicUtils.parseHex(gasLimit)); + return _gasLimit; + } + + public String getSha3Uncles() { + return sha3Uncles; + } + + public List getUncles() { + return uncles; + } + + public String getReceiptsRoot() { + return receiptsRoot; + } + + public String getTransactionsRoot() { + return transactionsRoot; + } + + public List getTransactions() { + return transactions; + } + // + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof BlockProxy)) + return false; + BlockProxy that = (BlockProxy) o; + return Objects.equals(number, that.number) && Objects.equals(hash, that.hash) + && Objects.equals(parentHash, that.parentHash) && Objects.equals(nonce, that.nonce); + } + + @Override + public int hashCode() { + return Objects.hash(number, hash, parentHash, nonce); + } + + @Override + public String toString() { + return "BlockProxy{" + + "number='" + number + '\'' + + ", hash='" + hash + '\'' + + ", parentHash='" + parentHash + '\'' + + ", stateRoot='" + stateRoot + '\'' + + ", size='" + size + '\'' + + ", difficulty='" + difficulty + '\'' + + ", totalDifficulty='" + totalDifficulty + '\'' + + ", timestamp='" + timestamp + '\'' + + ", miner='" + miner + '\'' + + ", nonce='" + nonce + '\'' + + ", extraData='" + extraData + '\'' + + ", logsBloom='" + logsBloom + '\'' + + ", mixHash='" + mixHash + '\'' + + ", gasUsed='" + gasUsed + '\'' + + ", gasLimit='" + gasLimit + '\'' + + ", sha3Uncles='" + sha3Uncles + '\'' + + ", uncles=" + uncles + + ", receiptsRoot='" + receiptsRoot + '\'' + + ", transactionsRoot='" + transactionsRoot + '\'' + + ", transactions=" + transactions + + '}'; + } + + @Override + public int compareTo(@NotNull BlockProxy o) { + return Long.compare(getNumber(), o.getNumber()); + } + + public static BlockProxyBuilder builder() { + return new BlockProxyBuilder(); + } + + public static final class BlockProxyBuilder { + + private Long number; + private String hash; + private String parentHash; + private String stateRoot; + private Long size; + private String difficulty; + private String totalDifficulty; + private LocalDateTime timestamp; + private String miner; + private String nonce; + private String extraData; + private String logsBloom; + private String mixHash; + private Wei gasUsed; + private Wei gasLimit; + private String sha3Uncles; + private List uncles; + private String receiptsRoot; + private String transactionsRoot; + private List transactions; + + private BlockProxyBuilder() {} + + public BlockProxyBuilder withNumber(Long number) { + this.number = number; + return this; + } + + public BlockProxyBuilder withHash(String hash) { + this.hash = hash; + return this; + } + + public BlockProxyBuilder withParentHash(String parentHash) { + this.parentHash = parentHash; + return this; + } + + public BlockProxyBuilder withStateRoot(String stateRoot) { + this.stateRoot = stateRoot; + return this; + } + + public BlockProxyBuilder withSize(Long size) { + this.size = size; + return this; + } + + public BlockProxyBuilder withDifficulty(String difficulty) { + this.difficulty = difficulty; + return this; + } + + public BlockProxyBuilder withTotalDifficulty(String totalDifficulty) { + this.totalDifficulty = totalDifficulty; + return this; + } + + public BlockProxyBuilder withTimestamp(LocalDateTime timestamp) { + this.timestamp = timestamp; + return this; + } + + public BlockProxyBuilder withMiner(String miner) { + this.miner = miner; + return this; + } + + public BlockProxyBuilder withNonce(String nonce) { + this.nonce = nonce; + return this; + } + + public BlockProxyBuilder withExtraData(String extraData) { + this.extraData = extraData; + return this; + } + + public BlockProxyBuilder withLogsBloom(String logsBloom) { + this.logsBloom = logsBloom; + return this; + } + + public BlockProxyBuilder withMixHash(String mixHash) { + this.mixHash = mixHash; + return this; + } + + public BlockProxyBuilder withGasUsed(Wei gasUsed) { + this.gasUsed = gasUsed; + return this; + } + + public BlockProxyBuilder withGasLimit(Wei gasLimit) { + this.gasLimit = gasLimit; + return this; + } + + public BlockProxyBuilder withSha3Uncles(String sha3Uncles) { + this.sha3Uncles = sha3Uncles; + return this; + } + + public BlockProxyBuilder withUncles(List uncles) { + this.uncles = uncles; + return this; + } + + public BlockProxyBuilder withReceiptsRoot(String receiptsRoot) { + this.receiptsRoot = receiptsRoot; + return this; + } + + public BlockProxyBuilder withTransactionsRoot(String transactionsRoot) { + this.transactionsRoot = transactionsRoot; + return this; + } + + public BlockProxyBuilder withTransactions(List transactions) { + this.transactions = transactions; + return this; + } + + public BlockProxy build() { + BlockProxy blockProxy = new BlockProxy(); + blockProxy.mixHash = this.mixHash; + blockProxy.totalDifficulty = this.totalDifficulty; + blockProxy.nonce = this.nonce; + blockProxy.uncles = this.uncles; + blockProxy.transactionsRoot = this.transactionsRoot; + blockProxy.number = String.valueOf(this.number); + blockProxy.logsBloom = this.logsBloom; + blockProxy.receiptsRoot = this.receiptsRoot; + blockProxy.hash = this.hash; + blockProxy.parentHash = this.parentHash; + blockProxy._size = this.size; + blockProxy.difficulty = this.difficulty; + if (this.gasLimit != null) { + blockProxy._gasLimit = this.gasLimit; + } + if (this.gasUsed != null) { + blockProxy._gasUsed = this.gasUsed; + } + blockProxy.size = String.valueOf(this.size); + blockProxy.extraData = this.extraData; + blockProxy.stateRoot = this.stateRoot; + blockProxy.sha3Uncles = this.sha3Uncles; + blockProxy.miner = this.miner; + if (this.timestamp != null) { + blockProxy.timestamp = String.valueOf(this.timestamp.toEpochSecond(ZoneOffset.UTC)); + blockProxy._timestamp = this.timestamp; + } + blockProxy.transactions = this.transactions; + blockProxy._number = this.number; + return blockProxy; + } + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java new file mode 100644 index 0000000..e6df01c --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java @@ -0,0 +1,237 @@ +package io.goodforgod.api.etherscan.model.proxy; + +import com.google.gson.annotations.Expose; +import io.goodforgod.api.etherscan.model.Log; +import io.goodforgod.api.etherscan.model.Wei; +import io.goodforgod.api.etherscan.util.BasicUtils; +import java.util.List; +import java.util.Objects; + +/** + * @author GoodforGod + * @since 03.11.2018 + */ +public class ReceiptProxy { + + private String root; + private String from; + private String to; + private String blockNumber; + @Expose(serialize = false, deserialize = false) + private Long _blockNumber; + private String blockHash; + private String transactionHash; + private String transactionIndex; + @Expose(serialize = false, deserialize = false) + private Long _transactionIndex; + private String gasUsed; + @Expose(serialize = false, deserialize = false) + private Wei _gasUsed; + private String cumulativeGasUsed; + @Expose(serialize = false, deserialize = false) + private Wei _cumulativeGasUsed; + private String contractAddress; + + private List logs; + private String logsBloom; + + protected ReceiptProxy() {} + + // + public String getRoot() { + return root; + } + + public String getFrom() { + return from; + } + + public String getTo() { + return to; + } + + public Long getBlockNumber() { + if (_blockNumber == null && !BasicUtils.isEmpty(blockNumber)) + _blockNumber = BasicUtils.parseHex(blockNumber).longValue(); + return _blockNumber; + } + + public String getBlockHash() { + return blockHash; + } + + public String getTransactionHash() { + return transactionHash; + } + + public Long getTransactionIndex() { + if (_transactionIndex == null && !BasicUtils.isEmpty(transactionIndex)) + _transactionIndex = BasicUtils.parseHex(transactionIndex).longValue(); + return _transactionIndex; + } + + public Wei getGasUsed() { + if (_gasUsed == null && !BasicUtils.isEmpty(gasUsed)) + _gasUsed = Wei.ofWei(BasicUtils.parseHex(gasUsed)); + return _gasUsed; + } + + public Wei getGasUsedCumulative() { + if (_cumulativeGasUsed == null && !BasicUtils.isEmpty(cumulativeGasUsed)) + _cumulativeGasUsed = Wei.ofWei(BasicUtils.parseHex(cumulativeGasUsed)); + return _cumulativeGasUsed; + } + + public String getContractAddress() { + return contractAddress; + } + + public List getLogs() { + return logs; + } + + public String getLogsBloom() { + return logsBloom; + } + // + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof ReceiptProxy)) + return false; + ReceiptProxy that = (ReceiptProxy) o; + return Objects.equals(blockNumber, that.blockNumber) && Objects.equals(blockHash, that.blockHash) + && Objects.equals(transactionHash, that.transactionHash) + && Objects.equals(transactionIndex, that.transactionIndex); + } + + @Override + public int hashCode() { + return Objects.hash(blockNumber, blockHash, transactionHash, transactionIndex); + } + + @Override + public String toString() { + return "ReceiptProxy{" + + "root='" + root + '\'' + + ", from='" + from + '\'' + + ", to='" + to + '\'' + + ", blockNumber='" + blockNumber + '\'' + + ", blockHash='" + blockHash + '\'' + + ", transactionHash='" + transactionHash + '\'' + + ", transactionIndex='" + transactionIndex + '\'' + + ", gasUsed='" + gasUsed + '\'' + + ", cumulativeGasUsed='" + cumulativeGasUsed + '\'' + + ", contractAddress='" + contractAddress + '\'' + + ", logs=" + logs + + ", logsBloom='" + logsBloom + '\'' + + '}'; + } + + public static ReceiptProxyBuilder builder() { + return new ReceiptProxyBuilder(); + } + + public static final class ReceiptProxyBuilder { + + private String root; + private String from; + private String to; + private Long blockNumber; + private String blockHash; + private String transactionHash; + private Long transactionIndex; + private Wei gasUsed; + private Wei cumulativeGasUsed; + private String contractAddress; + private List logs; + private String logsBloom; + + private ReceiptProxyBuilder() {} + + public ReceiptProxyBuilder withRoot(String root) { + this.root = root; + return this; + } + + public ReceiptProxyBuilder withFrom(String from) { + this.from = from; + return this; + } + + public ReceiptProxyBuilder withTo(String to) { + this.to = to; + return this; + } + + public ReceiptProxyBuilder withBlockNumber(Long blockNumber) { + this.blockNumber = blockNumber; + return this; + } + + public ReceiptProxyBuilder withBlockHash(String blockHash) { + this.blockHash = blockHash; + return this; + } + + public ReceiptProxyBuilder withTransactionHash(String transactionHash) { + this.transactionHash = transactionHash; + return this; + } + + public ReceiptProxyBuilder withTransactionIndex(Long transactionIndex) { + this.transactionIndex = transactionIndex; + return this; + } + + public ReceiptProxyBuilder withGasUsed(Wei gasUsed) { + this.gasUsed = gasUsed; + return this; + } + + public ReceiptProxyBuilder withCumulativeGasUsed(Wei cumulativeGasUsed) { + this.cumulativeGasUsed = cumulativeGasUsed; + return this; + } + + public ReceiptProxyBuilder withContractAddress(String contractAddress) { + this.contractAddress = contractAddress; + return this; + } + + public ReceiptProxyBuilder withLogs(List logs) { + this.logs = logs; + return this; + } + + public ReceiptProxyBuilder withLogsBloom(String logsBloom) { + this.logsBloom = logsBloom; + return this; + } + + public ReceiptProxy build() { + ReceiptProxy receiptProxy = new ReceiptProxy(); + receiptProxy.logsBloom = this.logsBloom; + receiptProxy.transactionHash = this.transactionHash; + receiptProxy.blockNumber = String.valueOf(this.blockNumber); + receiptProxy.from = this.from; + receiptProxy._transactionIndex = this.transactionIndex; + receiptProxy.blockHash = this.blockHash; + receiptProxy.root = this.root; + receiptProxy.contractAddress = this.contractAddress; + if (this.gasUsed != null) { + receiptProxy._gasUsed = this.gasUsed; + } + receiptProxy.logs = this.logs; + receiptProxy.to = this.to; + if (this.cumulativeGasUsed != null) { + receiptProxy._cumulativeGasUsed = this.cumulativeGasUsed; + } + receiptProxy.transactionIndex = String.valueOf(this.transactionIndex); + receiptProxy._blockNumber = this.blockNumber; + return receiptProxy; + } + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java new file mode 100644 index 0000000..70b4fd7 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java @@ -0,0 +1,274 @@ +package io.goodforgod.api.etherscan.model.proxy; + +import com.google.gson.annotations.Expose; +import io.goodforgod.api.etherscan.model.Wei; +import io.goodforgod.api.etherscan.util.BasicUtils; +import java.util.Objects; +import org.jetbrains.annotations.NotNull; + +/** + * @author GoodforGod + * @since 31.10.2018 + */ +public class TxProxy implements Comparable { + + private String to; + private String hash; + private String transactionIndex; + @Expose(deserialize = false, serialize = false) + private Long _transactionIndex; + private String from; + private String v; + private String input; + private String s; + private String r; + private String nonce; + @Expose(deserialize = false, serialize = false) + private Long _nonce; + private String value; + private String gas; + @Expose(deserialize = false, serialize = false) + private Wei _gas; + private String gasPrice; + @Expose(deserialize = false, serialize = false) + private Wei _gasPrice; + private String blockHash; + private String blockNumber; + @Expose(deserialize = false, serialize = false) + private Long _blockNumber; + + protected TxProxy() {} + + // + public String getTo() { + return to; + } + + public String getHash() { + return hash; + } + + public Long getTransactionIndex() { + if (_transactionIndex == null && !BasicUtils.isEmpty(transactionIndex)) + _transactionIndex = BasicUtils.parseHex(transactionIndex).longValue(); + return _transactionIndex; + } + + public String getFrom() { + return from; + } + + public Wei getGas() { + if (_gas == null && !BasicUtils.isEmpty(gas)) + _gas = Wei.ofWei(BasicUtils.parseHex(gas)); + return _gas; + } + + public String getV() { + return v; + } + + public String getInput() { + return input; + } + + public String getS() { + return s; + } + + public String getR() { + return r; + } + + public Long getNonce() { + if (_nonce == null && !BasicUtils.isEmpty(nonce)) + _nonce = BasicUtils.parseHex(nonce).longValue(); + return _nonce; + } + + public String getValue() { + return value; + } + + public Wei getGasPrice() { + if (_gasPrice == null && !BasicUtils.isEmpty(gasPrice)) + _gasPrice = Wei.ofWei(BasicUtils.parseHex(gasPrice)); + return _gasPrice; + } + + public String getBlockHash() { + return blockHash; + } + + public Long getBlockNumber() { + if (_blockNumber == null && !BasicUtils.isEmpty(blockNumber)) + _blockNumber = BasicUtils.parseHex(blockNumber).longValue(); + return _blockNumber; + } + // + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof TxProxy)) + return false; + TxProxy txProxy = (TxProxy) o; + return Objects.equals(hash, txProxy.hash) && Objects.equals(transactionIndex, txProxy.transactionIndex) + && Objects.equals(nonce, txProxy.nonce) && Objects.equals(blockHash, txProxy.blockHash) + && Objects.equals(blockNumber, txProxy.blockNumber); + } + + @Override + public int hashCode() { + return Objects.hash(hash, transactionIndex, nonce, blockHash, blockNumber); + } + + @Override + public String toString() { + return "TxProxy{" + + "to='" + to + '\'' + + ", hash='" + hash + '\'' + + ", transactionIndex='" + transactionIndex + '\'' + + ", from='" + from + '\'' + + ", v='" + v + '\'' + + ", input='" + input + '\'' + + ", s='" + s + '\'' + + ", r='" + r + '\'' + + ", nonce='" + nonce + '\'' + + ", value='" + value + '\'' + + ", gas='" + gas + '\'' + + ", gasPrice='" + gasPrice + '\'' + + ", blockHash='" + blockHash + '\'' + + ", blockNumber='" + blockNumber + '\'' + + '}'; + } + + @Override + public int compareTo(@NotNull TxProxy o) { + final int firstCompare = Long.compare(getBlockNumber(), o.getBlockNumber()); + return (firstCompare == 0) + ? Long.compare(getTransactionIndex(), o.getTransactionIndex()) + : firstCompare; + } + + public static TxProxyBuilder builder() { + return new TxProxyBuilder(); + } + + public static final class TxProxyBuilder { + + private String to; + private String hash; + private Long transactionIndex; + private String from; + private String v; + private String input; + private String s; + private String r; + private Long nonce; + private String value; + private Wei gas; + private Wei gasPrice; + private String blockHash; + private Long blockNumber; + + private TxProxyBuilder() {} + + public TxProxyBuilder withTo(String to) { + this.to = to; + return this; + } + + public TxProxyBuilder withHash(String hash) { + this.hash = hash; + return this; + } + + public TxProxyBuilder withTransactionIndex(Long transactionIndex) { + this.transactionIndex = transactionIndex; + return this; + } + + public TxProxyBuilder withFrom(String from) { + this.from = from; + return this; + } + + public TxProxyBuilder withV(String v) { + this.v = v; + return this; + } + + public TxProxyBuilder withInput(String input) { + this.input = input; + return this; + } + + public TxProxyBuilder withS(String s) { + this.s = s; + return this; + } + + public TxProxyBuilder withR(String r) { + this.r = r; + return this; + } + + public TxProxyBuilder withNonce(Long nonce) { + this.nonce = nonce; + return this; + } + + public TxProxyBuilder withValue(String value) { + this.value = value; + return this; + } + + public TxProxyBuilder withGas(Wei gas) { + this.gas = gas; + return this; + } + + public TxProxyBuilder withGasPrice(Wei gasPrice) { + this.gasPrice = gasPrice; + return this; + } + + public TxProxyBuilder withBlockHash(String blockHash) { + this.blockHash = blockHash; + return this; + } + + public TxProxyBuilder withBlockNumber(Long blockNumber) { + this.blockNumber = blockNumber; + return this; + } + + public TxProxy build() { + TxProxy txProxy = new TxProxy(); + txProxy.input = this.input; + if (this.gas != null) { + txProxy._gas = this.gas; + } + txProxy.s = this.s; + txProxy.blockHash = this.blockHash; + txProxy.to = this.to; + txProxy.r = this.r; + txProxy.transactionIndex = String.valueOf(this.transactionIndex); + txProxy._nonce = this.nonce; + txProxy.value = this.value; + txProxy.v = this.v; + txProxy.from = this.from; + txProxy.nonce = String.valueOf(this.nonce); + txProxy._transactionIndex = this.transactionIndex; + txProxy.blockNumber = String.valueOf(this.blockNumber); + txProxy._blockNumber = this.blockNumber; + txProxy.hash = this.hash; + if (this.gasPrice != null) { + txProxy._gasPrice = this.gasPrice; + } + return txProxy; + } + } +} diff --git a/src/main/java/io/api/etherscan/model/proxy/utility/BaseProxyTO.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BaseProxyTO.java similarity index 83% rename from src/main/java/io/api/etherscan/model/proxy/utility/BaseProxyTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BaseProxyTO.java index 52c886f..ef57193 100644 --- a/src/main/java/io/api/etherscan/model/proxy/utility/BaseProxyTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BaseProxyTO.java @@ -1,8 +1,6 @@ -package io.api.etherscan.model.proxy.utility; +package io.goodforgod.api.etherscan.model.proxy.utility; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 31.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/proxy/utility/BlockProxyTO.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BlockProxyTO.java similarity index 62% rename from src/main/java/io/api/etherscan/model/proxy/utility/BlockProxyTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BlockProxyTO.java index eb9d941..cf6c16b 100644 --- a/src/main/java/io/api/etherscan/model/proxy/utility/BlockProxyTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BlockProxyTO.java @@ -1,10 +1,8 @@ -package io.api.etherscan.model.proxy.utility; +package io.goodforgod.api.etherscan.model.proxy.utility; -import io.api.etherscan.model.proxy.BlockProxy; +import io.goodforgod.api.etherscan.model.proxy.BlockProxy; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 01.11.2018 */ diff --git a/src/main/java/io/api/etherscan/model/proxy/utility/ErrorProxyTO.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/ErrorProxyTO.java similarity index 78% rename from src/main/java/io/api/etherscan/model/proxy/utility/ErrorProxyTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/ErrorProxyTO.java index 57d2c07..9b14cec 100644 --- a/src/main/java/io/api/etherscan/model/proxy/utility/ErrorProxyTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/ErrorProxyTO.java @@ -1,8 +1,6 @@ -package io.api.etherscan.model.proxy.utility; +package io.goodforgod.api.etherscan.model.proxy.utility; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ diff --git a/src/main/java/io/api/etherscan/model/proxy/utility/StringProxyTO.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/StringProxyTO.java similarity index 73% rename from src/main/java/io/api/etherscan/model/proxy/utility/StringProxyTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/StringProxyTO.java index 90cd7c8..489d87b 100644 --- a/src/main/java/io/api/etherscan/model/proxy/utility/StringProxyTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/StringProxyTO.java @@ -1,8 +1,6 @@ -package io.api.etherscan.model.proxy.utility; +package io.goodforgod.api.etherscan.model.proxy.utility; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 31.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/proxy/utility/TxInfoProxyTO.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxInfoProxyTO.java similarity index 62% rename from src/main/java/io/api/etherscan/model/proxy/utility/TxInfoProxyTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxInfoProxyTO.java index c709f76..208cdbe 100644 --- a/src/main/java/io/api/etherscan/model/proxy/utility/TxInfoProxyTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxInfoProxyTO.java @@ -1,10 +1,8 @@ -package io.api.etherscan.model.proxy.utility; +package io.goodforgod.api.etherscan.model.proxy.utility; -import io.api.etherscan.model.proxy.ReceiptProxy; +import io.goodforgod.api.etherscan.model.proxy.ReceiptProxy; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ diff --git a/src/main/java/io/api/etherscan/model/proxy/utility/TxProxyTO.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxProxyTO.java similarity index 62% rename from src/main/java/io/api/etherscan/model/proxy/utility/TxProxyTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxProxyTO.java index 4140a62..0c084e7 100644 --- a/src/main/java/io/api/etherscan/model/proxy/utility/TxProxyTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxProxyTO.java @@ -1,10 +1,8 @@ -package io.api.etherscan.model.proxy.utility; +package io.goodforgod.api.etherscan.model.proxy.utility; -import io.api.etherscan.model.proxy.TxProxy; +import io.goodforgod.api.etherscan.model.proxy.TxProxy; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 01.11.2018 */ diff --git a/src/main/java/io/api/etherscan/model/query/LogOp.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogOp.java similarity index 86% rename from src/main/java/io/api/etherscan/model/query/LogOp.java rename to src/main/java/io/goodforgod/api/etherscan/model/query/LogOp.java index 0c0ebee..9136034 100644 --- a/src/main/java/io/api/etherscan/model/query/LogOp.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogOp.java @@ -1,4 +1,4 @@ -package io.api.etherscan.model.query; +package io.goodforgod.api.etherscan.model.query; /** * Part of The Event Log API diff --git a/src/main/java/io/goodforgod/api/etherscan/model/query/LogQuery.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQuery.java new file mode 100644 index 0000000..9d8ea5a --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQuery.java @@ -0,0 +1,51 @@ +package io.goodforgod.api.etherscan.model.query; + +import static io.goodforgod.api.etherscan.model.query.LogQueryBuilderImpl.MAX_BLOCK; +import static io.goodforgod.api.etherscan.model.query.LogQueryBuilderImpl.MIN_BLOCK; + +import io.goodforgod.api.etherscan.LogsAPI; +import org.jetbrains.annotations.NotNull; + +/** + * Final built container for The Event Log API + * EtherScan - API Descriptions ... + * + * @see LogQueryBuilderImpl + * @see LogsAPI + * @author GoodforGod + * @since 10.05.2023 + */ +public interface LogQuery { + + @NotNull + String params(); + + @NotNull + static Builder builder(@NotNull String address) { + return new LogQueryBuilderImpl(address, MIN_BLOCK, MAX_BLOCK); + } + + interface Builder { + + @NotNull + LogQuery.Builder withBlockFrom(long startBlock); + + @NotNull + LogQuery.Builder withBlockTo(long endBlock); + + @NotNull + LogTopicSingle withTopic(@NotNull String topic0); + + @NotNull + LogTopicTuple withTopic(@NotNull String topic0, @NotNull String topic1); + + @NotNull + LogTopicTriple withTopic(@NotNull String topic0, @NotNull String topic1, @NotNull String topic2); + + @NotNull + LogTopicQuadro withTopic(@NotNull String topic0, @NotNull String topic1, @NotNull String topic2, @NotNull String topic3); + + @NotNull + LogQuery build(); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryBuilderImpl.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryBuilderImpl.java new file mode 100644 index 0000000..549bd47 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryBuilderImpl.java @@ -0,0 +1,86 @@ +package io.goodforgod.api.etherscan.model.query; + +import io.goodforgod.api.etherscan.LogsAPI; +import io.goodforgod.api.etherscan.error.EtherScanLogQueryException; +import io.goodforgod.api.etherscan.util.BasicUtils; +import org.jetbrains.annotations.NotNull; + +/** + * Builder for The Event Log API + * + * @see LogsAPI + * @author GoodforGod + * @since 31.10.2018 + */ +final class LogQueryBuilderImpl implements LogQuery.Builder { + + static final long MIN_BLOCK = 0; + static final long MAX_BLOCK = 99999999999999999L; + + private final String address; + private final long startBlock, endBlock; + + LogQueryBuilderImpl(String address, long startBlock, long endBlock) { + BasicUtils.validateAddress(address); + this.address = address; + this.startBlock = startBlock; + this.endBlock = endBlock; + } + + @Override + public @NotNull LogQuery.Builder withBlockFrom(long startBlock) { + return new LogQueryBuilderImpl(this.address, startBlock, this.endBlock); + } + + @Override + public @NotNull LogQuery.Builder withBlockTo(long endBlock) { + return new LogQueryBuilderImpl(this.address, this.startBlock, endBlock); + } + + @Override + public @NotNull LogTopicSingle withTopic(@NotNull String topic0) { + if (BasicUtils.isNotHex(topic0)) + throw new EtherScanLogQueryException("topic0 can not be empty or non hex."); + return new LogTopicSingle(address, startBlock, endBlock, topic0); + } + + @Override + public @NotNull LogTopicTuple withTopic(@NotNull String topic0, @NotNull String topic1) { + if (BasicUtils.isNotHex(topic0)) + throw new EtherScanLogQueryException("topic0 can not be empty or non hex."); + if (BasicUtils.isNotHex(topic1)) + throw new EtherScanLogQueryException("topic1 can not be empty or non hex."); + return new LogTopicTuple(address, startBlock, endBlock, topic0, topic1); + } + + @Override + public @NotNull LogTopicTriple withTopic(@NotNull String topic0, @NotNull String topic1, @NotNull String topic2) { + if (BasicUtils.isNotHex(topic0)) + throw new EtherScanLogQueryException("topic0 can not be empty or non hex."); + if (BasicUtils.isNotHex(topic1)) + throw new EtherScanLogQueryException("topic1 can not be empty or non hex."); + if (BasicUtils.isNotHex(topic2)) + throw new EtherScanLogQueryException("topic2 can not be empty or non hex."); + return new LogTopicTriple(address, startBlock, endBlock, topic0, topic1, topic2); + } + + @Override + public @NotNull LogTopicQuadro + withTopic(@NotNull String topic0, @NotNull String topic1, @NotNull String topic2, @NotNull String topic3) { + if (BasicUtils.isNotHex(topic0)) + throw new EtherScanLogQueryException("topic0 can not be empty or non hex."); + if (BasicUtils.isNotHex(topic1)) + throw new EtherScanLogQueryException("topic1 can not be empty or non hex."); + if (BasicUtils.isNotHex(topic2)) + throw new EtherScanLogQueryException("topic2 can not be empty or non hex."); + if (BasicUtils.isNotHex(topic3)) + throw new EtherScanLogQueryException("topic3 can not be empty or non hex."); + + return new LogTopicQuadro(address, startBlock, endBlock, topic0, topic1, topic2, topic3); + } + + @Override + public @NotNull LogQuery build() throws EtherScanLogQueryException { + return new LogQueryImpl("&address=" + this.address + "&fromBlock=" + this.startBlock + "&toBlock=" + this.endBlock); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryImpl.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryImpl.java new file mode 100644 index 0000000..ef66e72 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryImpl.java @@ -0,0 +1,30 @@ +package io.goodforgod.api.etherscan.model.query; + +import io.goodforgod.api.etherscan.LogsAPI; +import org.jetbrains.annotations.NotNull; + +/** + * Final builded container for The Event Log API + * EtherScan - API Descriptions ... + * + * @see LogQueryBuilderImpl + * @see LogsAPI + * @author GoodforGod + * @since 31.10.2018 + */ +final class LogQueryImpl implements LogQuery { + + /** + * Final request parameter for api call + */ + private final String params; + + LogQueryImpl(String params) { + this.params = params; + } + + @Override + public @NotNull String params() { + return params; + } +} diff --git a/src/main/java/io/api/etherscan/model/query/impl/BaseLogQuery.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryParams.java similarity index 79% rename from src/main/java/io/api/etherscan/model/query/impl/BaseLogQuery.java rename to src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryParams.java index 2fc688a..ac77ae8 100644 --- a/src/main/java/io/api/etherscan/model/query/impl/BaseLogQuery.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryParams.java @@ -1,17 +1,18 @@ -package io.api.etherscan.model.query.impl; +package io.goodforgod.api.etherscan.model.query; -import io.api.etherscan.core.ILogsApi; +import io.goodforgod.api.etherscan.LogsAPI; /** * Base parameters for The Event Log API builder * - * @see LogQueryBuilder - * @see ILogsApi - * + * @see LogQueryBuilderImpl + * @see LogsAPI * @author GoodforGod * @since 31.10.2018 */ -abstract class BaseLogQuery { +final class LogQueryParams { + + private LogQueryParams() {} static final String FROM_BLOCK_PARAM = "&fromBlock="; static final String TO_BLOCK_PARAM = "&toBlock="; diff --git a/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicBuilder.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicBuilder.java new file mode 100644 index 0000000..715f869 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicBuilder.java @@ -0,0 +1,17 @@ +package io.goodforgod.api.etherscan.model.query; + +import org.jetbrains.annotations.NotNull; + +/** + * @see LogTopicSingle + * @see LogTopicTuple + * @see LogTopicTriple + * @see LogTopicQuadro + * @author GoodforGod + * @since 10.05.2023 + */ +public interface LogTopicBuilder { + + @NotNull + LogQuery build(); +} diff --git a/src/main/java/io/api/etherscan/model/query/impl/LogTopicQuadro.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicQuadro.java similarity index 64% rename from src/main/java/io/api/etherscan/model/query/impl/LogTopicQuadro.java rename to src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicQuadro.java index 1c2bf35..7fdd9db 100644 --- a/src/main/java/io/api/etherscan/model/query/impl/LogTopicQuadro.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicQuadro.java @@ -1,20 +1,20 @@ -package io.api.etherscan.model.query.impl; +package io.goodforgod.api.etherscan.model.query; -import io.api.etherscan.core.ILogsApi; -import io.api.etherscan.error.LogQueryException; -import io.api.etherscan.model.query.IQueryBuilder; -import io.api.etherscan.model.query.LogOp; +import static io.goodforgod.api.etherscan.model.query.LogQueryParams.*; + +import io.goodforgod.api.etherscan.LogsAPI; +import io.goodforgod.api.etherscan.error.EtherScanLogQueryException; +import org.jetbrains.annotations.NotNull; /** * Quadro topic parameter builder for The Event Log API * - * @see LogQueryBuilder - * @see ILogsApi - * + * @see LogQueryBuilderImpl + * @see LogsAPI * @author GoodforGod * @since 31.10.2018 */ -public class LogTopicQuadro extends BaseLogQuery implements IQueryBuilder { +public final class LogTopicQuadro implements LogTopicBuilder { private final String address; private final long startBlock, endBlock; @@ -22,8 +22,13 @@ public class LogTopicQuadro extends BaseLogQuery implements IQueryBuilder { private LogOp topic0_1_opr, topic1_2_opr, topic2_3_opr, topic0_2_opr, topic0_3_opr, topic1_3_opr; - LogTopicQuadro(String address, long startBlock, long endBlock, - String topic0, String topic1, String topic2, String topic3) { + LogTopicQuadro(String address, + long startBlock, + long endBlock, + String topic0, + String topic1, + String topic2, + String topic3) { this.address = address; this.startBlock = startBlock; this.endBlock = endBlock; @@ -64,21 +69,21 @@ public LogTopicQuadro setOpTopic1_3(LogOp topic1_3_opr) { } @Override - public LogQuery build() { + public @NotNull LogQuery build() { if (topic0_1_opr == null) - throw new LogQueryException("topic0_1_opr can not be null."); + throw new EtherScanLogQueryException("topic0_1_opr can not be null."); if (topic0_2_opr == null) - throw new LogQueryException("topic0_2_opr can not be null."); + throw new EtherScanLogQueryException("topic0_2_opr can not be null."); if (topic0_3_opr == null) - throw new LogQueryException("topic0_3_opr can not be null."); + throw new EtherScanLogQueryException("topic0_3_opr can not be null."); if (topic1_2_opr == null) - throw new LogQueryException("topic1_2_opr can not be null."); + throw new EtherScanLogQueryException("topic1_2_opr can not be null."); if (topic2_3_opr == null) - throw new LogQueryException("topic2_3_opr can not be null."); + throw new EtherScanLogQueryException("topic2_3_opr can not be null."); if (topic1_3_opr == null) - throw new LogQueryException("topic1_3_opr can not be null."); + throw new EtherScanLogQueryException("topic1_3_opr can not be null."); - return new LogQuery(ADDRESS_PARAM + address + return new LogQueryImpl(ADDRESS_PARAM + address + FROM_BLOCK_PARAM + startBlock + TO_BLOCK_PARAM + endBlock + TOPIC_0_PARAM + topic0 + TOPIC_1_PARAM + topic1 diff --git a/src/main/java/io/api/etherscan/model/query/impl/LogTopicSingle.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicSingle.java similarity index 53% rename from src/main/java/io/api/etherscan/model/query/impl/LogTopicSingle.java rename to src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicSingle.java index 2c19d61..a736ffa 100644 --- a/src/main/java/io/api/etherscan/model/query/impl/LogTopicSingle.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicSingle.java @@ -1,19 +1,20 @@ -package io.api.etherscan.model.query.impl; +package io.goodforgod.api.etherscan.model.query; -import io.api.etherscan.core.ILogsApi; -import io.api.etherscan.error.LogQueryException; -import io.api.etherscan.model.query.IQueryBuilder; +import static io.goodforgod.api.etherscan.model.query.LogQueryParams.*; + +import io.goodforgod.api.etherscan.LogsAPI; +import io.goodforgod.api.etherscan.error.EtherScanLogQueryException; +import org.jetbrains.annotations.NotNull; /** * Single topic parameter builder for The Event Log API * - * @see LogQueryBuilder - * @see ILogsApi - * + * @see LogQueryBuilderImpl + * @see LogsAPI * @author GoodforGod * @since 31.10.2018 */ -public class LogTopicSingle extends BaseLogQuery implements IQueryBuilder { +public final class LogTopicSingle implements LogTopicBuilder { private final String address; private final long startBlock, endBlock; @@ -28,8 +29,8 @@ public class LogTopicSingle extends BaseLogQuery implements IQueryBuilder { } @Override - public LogQuery build() throws LogQueryException { - return new LogQuery(ADDRESS_PARAM + address + public @NotNull LogQuery build() throws EtherScanLogQueryException { + return new LogQueryImpl(ADDRESS_PARAM + address + FROM_BLOCK_PARAM + startBlock + TO_BLOCK_PARAM + endBlock + TOPIC_0_PARAM + topic0); } diff --git a/src/main/java/io/api/etherscan/model/query/impl/LogTopicTriple.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTriple.java similarity index 59% rename from src/main/java/io/api/etherscan/model/query/impl/LogTopicTriple.java rename to src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTriple.java index aa54740..ac9efb8 100644 --- a/src/main/java/io/api/etherscan/model/query/impl/LogTopicTriple.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTriple.java @@ -1,20 +1,20 @@ -package io.api.etherscan.model.query.impl; +package io.goodforgod.api.etherscan.model.query; -import io.api.etherscan.core.ILogsApi; -import io.api.etherscan.error.LogQueryException; -import io.api.etherscan.model.query.IQueryBuilder; -import io.api.etherscan.model.query.LogOp; +import static io.goodforgod.api.etherscan.model.query.LogQueryParams.*; + +import io.goodforgod.api.etherscan.LogsAPI; +import io.goodforgod.api.etherscan.error.EtherScanLogQueryException; +import org.jetbrains.annotations.NotNull; /** * Triple topic parameter builder for The Event Log API * - * @see LogQueryBuilder - * @see ILogsApi - * + * @see LogQueryBuilderImpl + * @see LogsAPI * @author GoodforGod * @since 31.10.2018 */ -public class LogTopicTriple extends BaseLogQuery implements IQueryBuilder { +public final class LogTopicTriple implements LogTopicBuilder { private final String address; private final long startBlock, endBlock; @@ -22,8 +22,12 @@ public class LogTopicTriple extends BaseLogQuery implements IQueryBuilder { private LogOp topic0_1_opr, topic1_2_opr, topic0_2_opr; - LogTopicTriple(String address, long startBlock, long endBlock, - String topic0, String topic1, String topic2) { + LogTopicTriple(String address, + long startBlock, + long endBlock, + String topic0, + String topic1, + String topic2) { this.address = address; this.startBlock = startBlock; this.endBlock = endBlock; @@ -48,15 +52,15 @@ public LogTopicTriple setOpTopic1_2(LogOp topic1_2_opr) { } @Override - public LogQuery build() throws LogQueryException { + public @NotNull LogQuery build() throws EtherScanLogQueryException { if (topic0_1_opr == null) - throw new LogQueryException("topic0_1_opr can not be null."); + throw new EtherScanLogQueryException("topic0_1_opr can not be null."); if (topic0_2_opr == null) - throw new LogQueryException("topic0_2_opr can not be null."); + throw new EtherScanLogQueryException("topic0_2_opr can not be null."); if (topic1_2_opr == null) - throw new LogQueryException("topic1_2_opr can not be null."); + throw new EtherScanLogQueryException("topic1_2_opr can not be null."); - return new LogQuery(ADDRESS_PARAM + address + return new LogQueryImpl(ADDRESS_PARAM + address + FROM_BLOCK_PARAM + startBlock + TO_BLOCK_PARAM + endBlock + TOPIC_0_PARAM + topic0 + TOPIC_1_PARAM + topic1 diff --git a/src/main/java/io/api/etherscan/model/query/impl/LogTopicTuple.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTuple.java similarity index 52% rename from src/main/java/io/api/etherscan/model/query/impl/LogTopicTuple.java rename to src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTuple.java index 8f069f1..2ef2bba 100644 --- a/src/main/java/io/api/etherscan/model/query/impl/LogTopicTuple.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTuple.java @@ -1,20 +1,20 @@ -package io.api.etherscan.model.query.impl; +package io.goodforgod.api.etherscan.model.query; -import io.api.etherscan.core.ILogsApi; -import io.api.etherscan.error.LogQueryException; -import io.api.etherscan.model.query.IQueryBuilder; -import io.api.etherscan.model.query.LogOp; +import static io.goodforgod.api.etherscan.model.query.LogQueryParams.*; + +import io.goodforgod.api.etherscan.LogsAPI; +import io.goodforgod.api.etherscan.error.EtherScanLogQueryException; +import org.jetbrains.annotations.NotNull; /** * Tuple topic parameter builder for The Event Log API * - * @see LogQueryBuilder - * @see ILogsApi - * + * @see LogQueryBuilderImpl + * @see LogsAPI * @author GoodforGod * @since 31.10.2018 */ -public class LogTopicTuple extends BaseLogQuery implements IQueryBuilder { +public final class LogTopicTuple implements LogTopicBuilder { private final String address; private final long startBlock, endBlock; @@ -22,8 +22,11 @@ public class LogTopicTuple extends BaseLogQuery implements IQueryBuilder { private LogOp topic0_1_opr; - LogTopicTuple(String address, long startBlock, long endBlock, - String topic0, String topic1) { + LogTopicTuple(String address, + long startBlock, + long endBlock, + String topic0, + String topic1) { this.address = address; this.startBlock = startBlock; this.endBlock = endBlock; @@ -37,11 +40,11 @@ public LogTopicTuple setOpTopic0_1(LogOp topic0_1_opr) { } @Override - public LogQuery build() throws LogQueryException { + public @NotNull LogQuery build() throws EtherScanLogQueryException { if (topic0_1_opr == null) - throw new LogQueryException("topic0_1_opr can not be null."); + throw new EtherScanLogQueryException("topic0_1_opr can not be null."); - return new LogQuery(ADDRESS_PARAM + address + return new LogQueryImpl(ADDRESS_PARAM + address + FROM_BLOCK_PARAM + startBlock + TO_BLOCK_PARAM + endBlock + TOPIC_0_PARAM + topic0 + TOPIC_1_PARAM + topic1 diff --git a/src/main/java/io/api/etherscan/model/utility/BalanceResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/BalanceResponseTO.java similarity index 65% rename from src/main/java/io/api/etherscan/model/utility/BalanceResponseTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/response/BalanceResponseTO.java index 6b23de4..189ed87 100644 --- a/src/main/java/io/api/etherscan/model/utility/BalanceResponseTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/BalanceResponseTO.java @@ -1,8 +1,6 @@ -package io.api.etherscan.model.utility; +package io.goodforgod.api.etherscan.model.response; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 29.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/utility/BalanceTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/BalanceTO.java similarity index 80% rename from src/main/java/io/api/etherscan/model/utility/BalanceTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/response/BalanceTO.java index 8d9d9b7..8f56790 100644 --- a/src/main/java/io/api/etherscan/model/utility/BalanceTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/BalanceTO.java @@ -1,8 +1,6 @@ -package io.api.etherscan.model.utility; +package io.goodforgod.api.etherscan.model.response; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 29.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/utility/BaseListResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/BaseListResponseTO.java similarity index 78% rename from src/main/java/io/api/etherscan/model/utility/BaseListResponseTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/response/BaseListResponseTO.java index 28f01f3..0d1b06c 100644 --- a/src/main/java/io/api/etherscan/model/utility/BaseListResponseTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/BaseListResponseTO.java @@ -1,10 +1,8 @@ -package io.api.etherscan.model.utility; +package io.goodforgod.api.etherscan.model.response; import java.util.List; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 30.10.2018 */ diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/BaseResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/BaseResponseTO.java new file mode 100644 index 0000000..46c6ca0 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/BaseResponseTO.java @@ -0,0 +1,23 @@ +package io.goodforgod.api.etherscan.model.response; + +import io.goodforgod.api.etherscan.util.BasicUtils; + +/** + * @author GoodforGod + * @since 29.10.2018 + */ +public abstract class BaseResponseTO { + + String status; + String message; + + public int getStatus() { + return BasicUtils.isEmpty(status) + ? -1 + : Integer.parseInt(status); + } + + public String getMessage() { + return message; + } +} diff --git a/src/main/java/io/api/etherscan/model/utility/BlockParam.java b/src/main/java/io/goodforgod/api/etherscan/model/response/BlockParam.java similarity index 86% rename from src/main/java/io/api/etherscan/model/utility/BlockParam.java rename to src/main/java/io/goodforgod/api/etherscan/model/response/BlockParam.java index 0f027ec..b2dbd32 100644 --- a/src/main/java/io/api/etherscan/model/utility/BlockParam.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/BlockParam.java @@ -1,8 +1,6 @@ -package io.api.etherscan.model.utility; +package io.goodforgod.api.etherscan.model.response; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 31.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/utility/BlockResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/BlockResponseTO.java similarity index 54% rename from src/main/java/io/api/etherscan/model/utility/BlockResponseTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/response/BlockResponseTO.java index 0d63184..363612b 100644 --- a/src/main/java/io/api/etherscan/model/utility/BlockResponseTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/BlockResponseTO.java @@ -1,10 +1,8 @@ -package io.api.etherscan.model.utility; +package io.goodforgod.api.etherscan.model.response; -import io.api.etherscan.model.Block; +import io.goodforgod.api.etherscan.model.Block; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 30.10.2018 */ diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/EthSupplyResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/EthSupplyResponseTO.java new file mode 100644 index 0000000..edbc2e3 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/EthSupplyResponseTO.java @@ -0,0 +1,16 @@ +package io.goodforgod.api.etherscan.model.response; + +import io.goodforgod.api.etherscan.model.EthSupply; + +/** + * @author GoodforGod + * @since 14.05.2023 + */ +public class EthSupplyResponseTO extends BaseResponseTO { + + private EthSupply result; + + public EthSupply getResult() { + return result; + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/GasEstimateResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/GasEstimateResponseTO.java new file mode 100644 index 0000000..96432f3 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/GasEstimateResponseTO.java @@ -0,0 +1,14 @@ +package io.goodforgod.api.etherscan.model.response; + +/** + * @author Abhay Gupta + * @since 14.11.2022 + */ +public class GasEstimateResponseTO extends BaseResponseTO { + + private String result; + + public String getResult() { + return result; + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/GasOracleResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/GasOracleResponseTO.java new file mode 100644 index 0000000..751854c --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/GasOracleResponseTO.java @@ -0,0 +1,16 @@ +package io.goodforgod.api.etherscan.model.response; + +import io.goodforgod.api.etherscan.model.GasOracle; + +/** + * @author Abhay Gupta + * @since 14.11.2022 + */ +public class GasOracleResponseTO extends BaseResponseTO { + + private GasOracle result; + + public GasOracle getResult() { + return result; + } +} diff --git a/src/main/java/io/api/etherscan/model/utility/LogResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/LogResponseTO.java similarity index 53% rename from src/main/java/io/api/etherscan/model/utility/LogResponseTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/response/LogResponseTO.java index bba1c24..748d155 100644 --- a/src/main/java/io/api/etherscan/model/utility/LogResponseTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/LogResponseTO.java @@ -1,10 +1,8 @@ -package io.api.etherscan.model.utility; +package io.goodforgod.api.etherscan.model.response; -import io.api.etherscan.model.Log; +import io.goodforgod.api.etherscan.model.Log; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 31.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/utility/PriceResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/PriceResponseTO.java similarity index 65% rename from src/main/java/io/api/etherscan/model/utility/PriceResponseTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/response/PriceResponseTO.java index 3179a73..e2f0b63 100644 --- a/src/main/java/io/api/etherscan/model/utility/PriceResponseTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/PriceResponseTO.java @@ -1,10 +1,8 @@ -package io.api.etherscan.model.utility; +package io.goodforgod.api.etherscan.model.response; -import io.api.etherscan.model.Price; +import io.goodforgod.api.etherscan.model.Price; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 30.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/utility/ReceiptStatusResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/ReceiptStatusResponseTO.java similarity index 77% rename from src/main/java/io/api/etherscan/model/utility/ReceiptStatusResponseTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/response/ReceiptStatusResponseTO.java index 87e3950..922c5e2 100644 --- a/src/main/java/io/api/etherscan/model/utility/ReceiptStatusResponseTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/ReceiptStatusResponseTO.java @@ -1,8 +1,6 @@ -package io.api.etherscan.model.utility; +package io.goodforgod.api.etherscan.model.response; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ diff --git a/src/main/java/io/api/etherscan/model/utility/ReceiptStatusTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/ReceiptStatusTO.java similarity index 72% rename from src/main/java/io/api/etherscan/model/utility/ReceiptStatusTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/response/ReceiptStatusTO.java index 6b7995d..4f4717c 100644 --- a/src/main/java/io/api/etherscan/model/utility/ReceiptStatusTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/ReceiptStatusTO.java @@ -1,8 +1,6 @@ -package io.api.etherscan.model.utility; +package io.goodforgod.api.etherscan.model.response; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ diff --git a/src/main/java/io/api/etherscan/model/utility/StatusResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/StatusResponseTO.java similarity index 66% rename from src/main/java/io/api/etherscan/model/utility/StatusResponseTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/response/StatusResponseTO.java index bc10eb7..6847930 100644 --- a/src/main/java/io/api/etherscan/model/utility/StatusResponseTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/StatusResponseTO.java @@ -1,10 +1,8 @@ -package io.api.etherscan.model.utility; +package io.goodforgod.api.etherscan.model.response; -import io.api.etherscan.model.Status; +import io.goodforgod.api.etherscan.model.Status; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 30.10.2018 */ diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/StringResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/StringResponseTO.java new file mode 100644 index 0000000..19fa0a1 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/StringResponseTO.java @@ -0,0 +1,50 @@ +package io.goodforgod.api.etherscan.model.response; + +/** + * @author GoodforGod + * @since 29.10.2018 + */ +public class StringResponseTO extends BaseResponseTO { + + private String result; + + public String getResult() { + return result; + } + + public static StringResponseBuilder builder() { + return new StringResponseBuilder(); + } + + public static final class StringResponseBuilder { + + private String status; + private String message; + private String result; + + private StringResponseBuilder() {} + + public StringResponseBuilder withStatus(String status) { + this.status = status; + return this; + } + + public StringResponseBuilder withMessage(String message) { + this.message = message; + return this; + } + + public StringResponseBuilder withResult(String result) { + this.result = result; + return this; + } + + public StringResponseTO build() { + StringResponseTO stringResponseTO = new StringResponseTO(); + stringResponseTO.status = this.status; + stringResponseTO.message = this.message; + stringResponseTO.result = this.result; + return stringResponseTO; + } + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc1155ResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc1155ResponseTO.java new file mode 100644 index 0000000..3bf9d49 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc1155ResponseTO.java @@ -0,0 +1,11 @@ +package io.goodforgod.api.etherscan.model.response; + +import io.goodforgod.api.etherscan.model.TxErc1155; + +/** + * @author GoodforGod + * @since 14.05.2023 + */ +public class TxErc1155ResponseTO extends BaseListResponseTO { + +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc20ResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc20ResponseTO.java new file mode 100644 index 0000000..d5d3f6e --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc20ResponseTO.java @@ -0,0 +1,11 @@ +package io.goodforgod.api.etherscan.model.response; + +import io.goodforgod.api.etherscan.model.TxErc20; + +/** + * @author GoodforGod + * @since 29.10.2018 + */ +public class TxErc20ResponseTO extends BaseListResponseTO { + +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc721ResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc721ResponseTO.java new file mode 100644 index 0000000..27518ae --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc721ResponseTO.java @@ -0,0 +1,11 @@ +package io.goodforgod.api.etherscan.model.response; + +import io.goodforgod.api.etherscan.model.TxErc721; + +/** + * @author GoodforGod + * @since 14.05.2023 + */ +public class TxErc721ResponseTO extends BaseListResponseTO { + +} diff --git a/src/main/java/io/api/etherscan/model/utility/TxInternalResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/TxInternalResponseTO.java similarity index 55% rename from src/main/java/io/api/etherscan/model/utility/TxInternalResponseTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/response/TxInternalResponseTO.java index d38a879..efcf4dd 100644 --- a/src/main/java/io/api/etherscan/model/utility/TxInternalResponseTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/TxInternalResponseTO.java @@ -1,10 +1,8 @@ -package io.api.etherscan.model.utility; +package io.goodforgod.api.etherscan.model.response; -import io.api.etherscan.model.TxInternal; +import io.goodforgod.api.etherscan.model.TxInternal; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 29.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/utility/TxResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/TxResponseTO.java similarity index 53% rename from src/main/java/io/api/etherscan/model/utility/TxResponseTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/response/TxResponseTO.java index 53cce38..f4bd3f0 100644 --- a/src/main/java/io/api/etherscan/model/utility/TxResponseTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/TxResponseTO.java @@ -1,10 +1,8 @@ -package io.api.etherscan.model.utility; +package io.goodforgod.api.etherscan.model.response; -import io.api.etherscan.model.Tx; +import io.goodforgod.api.etherscan.model.Tx; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 29.10.2018 */ diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/UncleBlockResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/UncleBlockResponseTO.java new file mode 100644 index 0000000..de94f9e --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/UncleBlockResponseTO.java @@ -0,0 +1,16 @@ +package io.goodforgod.api.etherscan.model.response; + +import io.goodforgod.api.etherscan.model.BlockUncle; + +/** + * @author GoodforGod + * @since 30.10.2018 + */ +public class UncleBlockResponseTO extends BaseResponseTO { + + private BlockUncle result; + + public BlockUncle getResult() { + return result; + } +} diff --git a/src/main/java/io/api/etherscan/util/BasicUtils.java b/src/main/java/io/goodforgod/api/etherscan/util/BasicUtils.java similarity index 74% rename from src/main/java/io/api/etherscan/util/BasicUtils.java rename to src/main/java/io/goodforgod/api/etherscan/util/BasicUtils.java index 96b855d..216ab62 100644 --- a/src/main/java/io/api/etherscan/util/BasicUtils.java +++ b/src/main/java/io/goodforgod/api/etherscan/util/BasicUtils.java @@ -1,15 +1,15 @@ -package io.api.etherscan.util; - -import io.api.etherscan.error.EtherScanException; -import io.api.etherscan.error.InvalidAddressException; -import io.api.etherscan.error.InvalidTxHashException; -import io.api.etherscan.model.utility.BaseResponseTO; -import io.api.etherscan.model.utility.BlockParam; -import org.jetbrains.annotations.NotNull; - +package io.goodforgod.api.etherscan.util; + +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.error.EtherScanInvalidTxHashException; +import io.goodforgod.api.etherscan.error.EtherScanResponseException; +import io.goodforgod.api.etherscan.model.response.BaseResponseTO; +import io.goodforgod.api.etherscan.model.response.BlockParam; +import io.goodforgod.api.etherscan.model.response.StringResponseTO; import java.math.BigInteger; import java.util.*; import java.util.regex.Pattern; +import org.jetbrains.annotations.NotNull; /** * Basic utils for library @@ -17,17 +17,17 @@ * @author GoodforGod * @since 28.10.2018 */ -public class BasicUtils { +public final class BasicUtils { - private static final int MAX_END_BLOCK = 999999999; + private BasicUtils() {} + + private static final int MAX_END_BLOCK = Integer.MAX_VALUE; private static final int MIN_START_BLOCK = 0; private static final Pattern ADDRESS_PATTERN = Pattern.compile("0x[a-zA-Z0-9]{40}"); private static final Pattern TXHASH_PATTERN = Pattern.compile("0x[a-zA-Z0-9]{64}"); private static final Pattern HEX_PATTERN = Pattern.compile("[a-zA-Z0-9]+"); - private BasicUtils() {} - public static boolean isEmpty(String value) { return value == null || value.isEmpty(); } @@ -79,7 +79,7 @@ public static BigInteger parseHex(String hex) { return BigInteger.valueOf(0); final String formatted = (hex.length() > 2 && hex.charAt(0) == '0' && hex.charAt(1) == 'x') - ? hex.substring(2, hex.length()) + ? hex.substring(2) : hex; return new BigInteger(formatted, 16); @@ -90,24 +90,29 @@ public static BigInteger parseHex(String hex) { public static void validateAddress(String address) { if (isNotAddress(address)) - throw new InvalidAddressException("Address [" + address + "] is not Ethereum based."); + throw new EtherScanInvalidAddressException("Address [" + address + "] is not Ethereum based."); } public static void validateTxHash(String txhash) { if (isNotTxHash(txhash)) - throw new InvalidTxHashException("TxHash [" + txhash + "] is not Ethereum based."); + throw new EtherScanInvalidTxHashException("TxHash [" + txhash + "] is not Ethereum based."); } public static void validateTxResponse(T response) { - if (response == null) - throw new EtherScanException("EtherScan responded with null value"); + if (response == null) { + final StringResponseTO emptyResponse = StringResponseTO.builder() + .withStatus("0") + .withMessage("EtherScan responded with null value") + .build(); + throw new EtherScanResponseException(emptyResponse, "EtherScan responded with null value"); + } if (response.getStatus() != 1) { if (response.getMessage() == null) { - throw new EtherScanException( + throw new EtherScanResponseException(response, "Unexpected Etherscan exception, no information from server about error, code " + response.getStatus()); } else if (!response.getMessage().startsWith("No tra") && !response.getMessage().startsWith("No rec")) { - throw new EtherScanException(response); + throw new EtherScanResponseException(response); } } } @@ -115,7 +120,7 @@ public static void validateTxResponse(T response) { public static void validateAddresses(List addresses) { for (String address : addresses) { if (isNotAddress(address)) - throw new InvalidAddressException("Address [" + address + "] is not Ethereum based."); + throw new EtherScanInvalidAddressException("Address [" + address + "] is not Ethereum based."); } } diff --git a/src/test/java/io/api/ApiRunner.java b/src/test/java/io/api/ApiRunner.java deleted file mode 100644 index 184a84e..0000000 --- a/src/test/java/io/api/ApiRunner.java +++ /dev/null @@ -1,60 +0,0 @@ -package io.api; - -import io.api.etherscan.core.impl.EtherScanApi; -import io.api.etherscan.manager.impl.QueueManager; -import io.api.etherscan.model.EthNetwork; -import org.junit.AfterClass; -import org.junit.Assert; - -public class ApiRunner extends Assert { - - private static final EtherScanApi api; - private static final EtherScanApi apiRopsten; - private static final EtherScanApi apiRinkeby; - private static final EtherScanApi apiKovan; - private static final String apiKey; - - static { - final String key = System.getenv("API_KEY"); - apiKey = (key == null || key.isEmpty()) - ? EtherScanApi.DEFAULT_KEY - : key; - - final QueueManager queueManager = (EtherScanApi.DEFAULT_KEY.equals(apiKey)) - ? QueueManager.DEFAULT_KEY_QUEUE - : new QueueManager(1, 1200L, 1200L, 0); - - api = new EtherScanApi(ApiRunner.apiKey, EthNetwork.MAINNET, queueManager); - apiKovan = new EtherScanApi(ApiRunner.apiKey, EthNetwork.KOVAN, queueManager); - apiRopsten = new EtherScanApi(ApiRunner.apiKey, EthNetwork.ROPSTEN, queueManager); - apiRinkeby = new EtherScanApi(ApiRunner.apiKey, EthNetwork.RINKEBY, queueManager); - } - - public static String getApiKey() { - return apiKey; - } - - public static EtherScanApi getApi() { - return api; - } - - public static EtherScanApi getApiRopsten() { - return apiRopsten; - } - - public static EtherScanApi getApiRinkeby() { - return apiRinkeby; - } - - public static EtherScanApi getApiKovan() { - return apiKovan; - } - - @AfterClass - public static void cleanup() throws Exception { - api.close(); - apiRopsten.close(); - apiRinkeby.close(); - apiKovan.close(); - } -} diff --git a/src/test/java/io/api/etherscan/EtherScanApiTest.java b/src/test/java/io/api/etherscan/EtherScanApiTest.java deleted file mode 100644 index be49435..0000000 --- a/src/test/java/io/api/etherscan/EtherScanApiTest.java +++ /dev/null @@ -1,86 +0,0 @@ -package io.api.etherscan; - -import io.api.ApiRunner; -import io.api.etherscan.core.impl.EtherScanApi; -import io.api.etherscan.error.ApiException; -import io.api.etherscan.error.ApiKeyException; -import io.api.etherscan.error.ApiTimeoutException; -import io.api.etherscan.executor.IHttpExecutor; -import io.api.etherscan.executor.impl.HttpExecutor; -import io.api.etherscan.model.Balance; -import io.api.etherscan.model.Block; -import io.api.etherscan.model.EthNetwork; -import org.junit.Test; - -import java.util.List; -import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; - -/** - * @author GoodforGod - * @since 05.11.2018 - */ -public class EtherScanApiTest extends ApiRunner { - - private final EthNetwork network = EthNetwork.KOVAN; - private final String validKey = "YourKey"; - - @Test - public void validKey() { - EtherScanApi api = new EtherScanApi(validKey, network); - assertNotNull(api); - } - - @Test(expected = ApiKeyException.class) - public void emptyKey() { - new EtherScanApi(""); - } - - @Test(expected = ApiKeyException.class) - public void blankKey() { - new EtherScanApi(" ", network); - } - - @Test(expected = ApiException.class) - public void nullNetwork() { - EtherScanApi api = new EtherScanApi(validKey, null); - assertNotNull(api); - } - - @Test - public void noTimeoutOnRead() { - Supplier supplier = () -> new HttpExecutor(300); - EtherScanApi api = new EtherScanApi(EthNetwork.MAINNET, supplier); - Balance balance = api.account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); - assertNotNull(balance); - } - - @Test - public void noTimeoutOnReadGroli() { - Supplier supplier = () -> new HttpExecutor(300); - Balance balance = getApi().account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); - assertNotNull(balance); - } - - @Test - public void noTimeoutOnReadTobalala() { - Supplier supplier = () -> new HttpExecutor(30000); - Balance balance = getApi().account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); - assertNotNull(balance); - } - - @Test - public void noTimeoutUnlimitedAwait() { - Balance balance = getApi().account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); - assertNotNull(balance); - } - - @Test(expected = ApiTimeoutException.class) - public void timeout() throws InterruptedException { - TimeUnit.SECONDS.sleep(5); - Supplier supplier = () -> new HttpExecutor(300, 300); - EtherScanApi api = new EtherScanApi(getApiKey(), EthNetwork.KOVAN, supplier); - List blocks = api.account().minedBlocks("0x0010f94b296A852aAac52EA6c5Ac72e03afD032D"); - assertNotNull(blocks); - } -} diff --git a/src/test/java/io/api/etherscan/account/AccountBalanceTest.java b/src/test/java/io/api/etherscan/account/AccountBalanceTest.java deleted file mode 100644 index 76aca68..0000000 --- a/src/test/java/io/api/etherscan/account/AccountBalanceTest.java +++ /dev/null @@ -1,74 +0,0 @@ -package io.api.etherscan.account; - -import io.api.ApiRunner; -import io.api.etherscan.core.impl.EtherScanApi; -import io.api.etherscan.error.InvalidAddressException; -import io.api.etherscan.model.Balance; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - -import java.util.Arrays; -import java.util.Collection; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 03.11.2018 - */ -@RunWith(Parameterized.class) -public class AccountBalanceTest extends ApiRunner { - - private final EtherScanApi api; - private final String addressCorrect; - private final String addressInvalid; - private final String addressNoResponse; - - public AccountBalanceTest(EtherScanApi api, String addressCorrect, String addressInvalid, String addressNoResponse) { - this.api = api; - this.addressCorrect = addressCorrect; - this.addressInvalid = addressInvalid; - this.addressNoResponse = addressNoResponse; - } - - @Parameters - public static Collection data() { - return Arrays.asList(new Object[][] { - { - getApi(), - "0x8d4426f94e42f721C7116E81d6688cd935cB3b4F", - "8d4426f94e42f721C7116E81d6688cd935cB3b4F", - "0x1d4426f94e42f721C7116E81d6688cd935cB3b4F" - } - }); - } - - @Test - public void correct() { - Balance balance = api.account().balance(addressCorrect); - assertNotNull(balance); - assertNotNull(balance.getWei()); - assertNotNull(balance.getMwei()); - assertNotNull(balance.getKwei()); - assertNotNull(balance.getGwei()); - assertNotNull(balance.getEther()); - assertNotNull(balance.getAddress()); - assertNotNull(balance.toString()); - } - - @Test(expected = InvalidAddressException.class) - public void invalidParamWithError() { - Balance balance = getApi().account().balance(addressInvalid); - } - - @Test - public void correctParamWithEmptyExpectedResult() { - Balance balance = api.account().balance(addressNoResponse); - assertNotNull(balance); - assertNotNull(balance.getWei()); - assertNotNull(balance.getAddress()); - assertEquals(0, balance.getWei().intValue()); - } -} diff --git a/src/test/java/io/api/etherscan/account/AccountMinedBlocksTest.java b/src/test/java/io/api/etherscan/account/AccountMinedBlocksTest.java deleted file mode 100644 index 3a46858..0000000 --- a/src/test/java/io/api/etherscan/account/AccountMinedBlocksTest.java +++ /dev/null @@ -1,90 +0,0 @@ -package io.api.etherscan.account; - -import io.api.ApiRunner; -import io.api.etherscan.core.impl.EtherScanApi; -import io.api.etherscan.error.InvalidAddressException; -import io.api.etherscan.model.Block; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 03.11.2018 - */ -@RunWith(Parameterized.class) -public class AccountMinedBlocksTest extends ApiRunner { - - private final EtherScanApi api; - private final int blocksMined; - private final String addressCorrect; - private final String addressInvalid; - private final String addressNoResponse; - - public AccountMinedBlocksTest(EtherScanApi api, - int blocksMined, - String addressCorrect, - String addressInvalid, - String addressNoResponse) { - this.api = api; - this.blocksMined = blocksMined; - this.addressCorrect = addressCorrect; - this.addressInvalid = addressInvalid; - this.addressNoResponse = addressNoResponse; - } - - @Parameters - public static Collection data() { - return Arrays.asList(new Object[][] { - { - getApi(), - 223, - "0xE4C6175183029A0f039bf2DFffa5C6e8F3cA9B23", - "xE4C6175183029A0f039bf2DFffa5C6e8F3cA9B23", - "0xE1C6175183029A0f039bf2DFffa5C6e8F3cA9B23", - } - }); - } - - @Test - public void correct() { - List blocks = api.account().minedBlocks(addressCorrect); - assertNotNull(blocks); - - assertEquals(blocksMined, blocks.size()); - assertBlocks(blocks); - assertNotNull(blocks.get(0).toString()); - - if (blocks.size() > 1) { - assertNotEquals(blocks.get(0), blocks.get(1)); - assertNotEquals(blocks.get(0).hashCode(), blocks.get(1).hashCode()); - } - } - - @Test(expected = InvalidAddressException.class) - public void invalidParamWithError() { - List txs = getApi().account().minedBlocks(addressInvalid); - } - - @Test - public void correctParamWithEmptyExpectedResult() { - List txs = api.account().minedBlocks(addressNoResponse); - assertNotNull(txs); - assertTrue(txs.isEmpty()); - } - - private void assertBlocks(List blocks) { - for (Block block : blocks) { - assertNotEquals(0, block.getBlockNumber()); - assertNotNull(block.getBlockReward()); - assertNotNull(block.getTimeStamp()); - } - } -} diff --git a/src/test/java/io/api/etherscan/account/AccountTokenBalanceTest.java b/src/test/java/io/api/etherscan/account/AccountTokenBalanceTest.java deleted file mode 100644 index 2794e95..0000000 --- a/src/test/java/io/api/etherscan/account/AccountTokenBalanceTest.java +++ /dev/null @@ -1,93 +0,0 @@ -package io.api.etherscan.account; - -import io.api.ApiRunner; -import io.api.etherscan.core.impl.EtherScanApi; -import io.api.etherscan.error.InvalidAddressException; -import io.api.etherscan.model.Balance; -import io.api.etherscan.model.TokenBalance; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - -import java.util.Arrays; -import java.util.Collection; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 03.11.2018 - */ -@RunWith(Parameterized.class) -public class AccountTokenBalanceTest extends ApiRunner { - - private final EtherScanApi api; - private final String contractValid; - private final String addressValid; - private final String contractInvalid; - private final String addressInvalid; - private final String addressEmpty; - - public AccountTokenBalanceTest(EtherScanApi api, - String contractValid, - String addressValid, - String contractInvalid, - String addressInvalid, - String addressEmpty) { - this.api = api; - this.contractValid = contractValid; - this.addressValid = addressValid; - this.contractInvalid = contractInvalid; - this.addressInvalid = addressInvalid; - this.addressEmpty = addressEmpty; - } - - @Parameters - public static Collection data() { - return Arrays.asList(new Object[][] { - { - getApi(), - "0x5EaC95ad5b287cF44E058dCf694419333b796123", - "0x5d807e7F124EC2103a59c5249187f772c0b8D6b2", - "0xEaC95ad5b287cF44E058dCf694419333b796123", - "0x5807e7F124EC2103a59c5249187f772c0b8D6b2", - "0x1d807e7F124EC2103a59c5249187f772c0b8D6b2", - } - }); - } - - @Test - public void correct() { - TokenBalance balance = api.account().balance(addressValid, contractValid); - assertNotNull(balance); - assertNotNull(balance.getWei()); - assertNotNull(balance.getAddress()); - assertNotNull(balance.getContract()); - assertNotNull(balance.toString()); - - TokenBalance balance2 = new TokenBalance("125161", balance.getWei(), balance.getContract()); - assertNotEquals(balance, balance2); - assertNotEquals(balance.hashCode(), balance2.hashCode()); - } - - @Test(expected = InvalidAddressException.class) - public void invalidAddressParamWithError() { - Balance balance = api.account().balance(addressInvalid, contractValid); - } - - @Test(expected = InvalidAddressException.class) - public void invalidContractParamWithError() { - Balance balance = api.account().balance(addressValid, contractInvalid); - } - - @Test - public void correctParamWithEmptyExpectedResult() { - TokenBalance balance = api.account().balance(addressEmpty, contractValid); - assertNotNull(balance); - assertNotNull(balance.getWei()); - assertNotNull(balance.getAddress()); - assertNotNull(balance.getContract()); - assertEquals(0, balance.getWei().intValue()); - } -} diff --git a/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java b/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java deleted file mode 100644 index 126fd90..0000000 --- a/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java +++ /dev/null @@ -1,96 +0,0 @@ -package io.api.etherscan.account; - -import io.api.ApiRunner; -import io.api.etherscan.core.impl.EtherScanApi; -import io.api.etherscan.error.InvalidTxHashException; -import io.api.etherscan.model.TxInternal; -import io.api.etherscan.util.BasicUtils; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 03.11.2018 - */ -@RunWith(Parameterized.class) -public class AccountTxInternalByHashTest extends ApiRunner { - - private final EtherScanApi api; - private final int txAmount; - private final String validTx; - private final String invalidTx; - private final String emptyTx; - - public AccountTxInternalByHashTest(EtherScanApi api, int txAmount, String validTx, String invalidTx, String emptyTx) { - this.api = api; - this.txAmount = txAmount; - this.validTx = validTx; - this.invalidTx = invalidTx; - this.emptyTx = emptyTx; - } - - @Parameters - public static Collection data() { - return Arrays.asList(new Object[][] { - { - getApi(), - 1, - "0x1b513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b", - "0xb513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b", - "0x2b513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b", - } - }); - } - - @Test - public void correct() { - List txs = api.account().txsInternalByHash(validTx); - assertNotNull(txs); - assertEquals(txAmount, txs.size()); - assertTxs(txs); - assertNotNull(txs.get(0).getFrom()); - assertNotNull(txs.get(0).getTimeStamp()); - assertNotNull(txs.get(0).getGas()); - assertNotNull(txs.get(0).getValue()); - assertNotNull(txs.get(0).getType()); - assertFalse(txs.get(0).haveError()); - assertFalse(txs.get(0).haveError()); - assertNotEquals("-1", txs.get(0).getTraceIdAsString()); - assertTrue(BasicUtils.isEmpty(txs.get(0).getErrCode())); - assertNotNull(txs.get(0).toString()); - - if (txs.size() > 9) { - assertNotEquals(txs.get(0), txs.get(9)); - assertNotEquals(txs.get(0).hashCode(), txs.get(9).hashCode()); - } - } - - @Test(expected = InvalidTxHashException.class) - public void invalidParamWithError() { - List txs = api.account().txsInternalByHash(invalidTx); - } - - @Test - public void correctParamWithEmptyExpectedResult() { - List txs = api.account().txsInternalByHash(emptyTx); - assertNotNull(txs); - assertTrue(txs.isEmpty()); - } - - private void assertTxs(List txs) { - for (TxInternal tx : txs) { - assertNotEquals(0, tx.getBlockNumber()); - assertNotNull(tx.getFrom()); - assertNotNull(tx.getTo()); - assertNotNull(tx.getTimeStamp()); - } - } -} diff --git a/src/test/java/io/api/etherscan/logs/LogQueryBuilderTest.java b/src/test/java/io/api/etherscan/logs/LogQueryBuilderTest.java deleted file mode 100644 index 85b35e8..0000000 --- a/src/test/java/io/api/etherscan/logs/LogQueryBuilderTest.java +++ /dev/null @@ -1,355 +0,0 @@ -package io.api.etherscan.logs; - -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidAddressException; -import io.api.etherscan.error.LogQueryException; -import io.api.etherscan.model.query.LogOp; -import io.api.etherscan.model.query.impl.LogQuery; -import io.api.etherscan.model.query.impl.LogQueryBuilder; -import io.api.etherscan.model.query.impl.LogTopicQuadro; -import org.junit.Test; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 03.11.2018 - */ -public class LogQueryBuilderTest extends ApiRunner { - - @Test - public void singleCorrect() { - LogQuery single = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") - .build(); - - assertNotNull(single); - assertNotNull(single.getParams()); - } - - @Test(expected = InvalidAddressException.class) - public void singleInCorrectAddress() { - LogQuery single = LogQueryBuilder.with("033990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") - .build(); - - assertNotNull(single); - assertNotNull(single.getParams()); - } - - @Test(expected = LogQueryException.class) - public void singleInCorrectTopic() { - LogQuery single = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("6516=") - .build(); - - assertNotNull(single); - assertNotNull(single.getParams()); - } - - @Test - public void tupleCorrect() { - LogQuery tuple = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224) - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - "0x72657075746174696f6e00000000000000000000000000000000000000000000") - .setOpTopic0_1(LogOp.AND) - .build(); - - assertNotNull(tuple); - assertNotNull(tuple.getParams()); - } - - @Test(expected = LogQueryException.class) - public void tupleInCorrectOp() { - LogQuery tuple = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224) - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - "0x72657075746174696f6e00000000000000000000000000000000000000000000") - .setOpTopic0_1(null) - .build(); - - assertNotNull(tuple); - assertNotNull(tuple.getParams()); - } - - @Test - public void tripleCorrect() { - LogQuery triple = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - "0x72657075746174696f6e00000000000000000000000000000000000000000000", - "0x72657075746174696f6e00000000000000000000000000000000000000000000") - .setOpTopic0_1(LogOp.AND) - .setOpTopic0_2(LogOp.OR) - .setOpTopic1_2(LogOp.AND) - .build(); - - assertNotNull(triple); - assertNotNull(triple.getParams()); - } - - @Test(expected = LogQueryException.class) - public void tripleInCorrectOp() { - LogQuery triple = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - "0x72657075746174696f6e00000000000000000000000000000000000000000000", - "0x72657075746174696f6e00000000000000000000000000000000000000000000") - .setOpTopic0_1(LogOp.AND) - .setOpTopic0_2(null) - .setOpTopic1_2(LogOp.AND) - .build(); - - assertNotNull(triple); - assertNotNull(triple.getParams()); - } - - @Test(expected = LogQueryException.class) - public void tripleInCorrectTopic1() { - LogQuery triple = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) - .topic(null, - "0x72657075746174696f6e00000000000000000000000000000000000000000000", - "0x72657075746174696f6e00000000000000000000000000000000000000000000") - .setOpTopic0_1(LogOp.AND) - .setOpTopic0_2(LogOp.AND) - .setOpTopic1_2(LogOp.AND) - .build(); - - assertNotNull(triple); - assertNotNull(triple.getParams()); - } - - @Test(expected = LogQueryException.class) - public void tripleInCorrectTopic2() { - LogQuery triple = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - null, - "0x72657075746174696f6e00000000000000000000000000000000000000000000") - .setOpTopic0_1(LogOp.AND) - .setOpTopic0_2(LogOp.AND) - .setOpTopic1_2(LogOp.AND) - .build(); - - assertNotNull(triple); - assertNotNull(triple.getParams()); - } - - @Test(expected = LogQueryException.class) - public void tripleInCorrectTopic3() { - LogQuery triple = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - "0x72657075746174696f6e00000000000000000000000000000000000000000000", - null) - .setOpTopic0_1(LogOp.AND) - .setOpTopic0_2(LogOp.AND) - .setOpTopic1_2(LogOp.AND) - .build(); - - assertNotNull(triple); - assertNotNull(triple.getParams()); - } - - @Test - public void quadroCorrect() { - LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - "0x72657075746174696f6e00000000000000000000000000000000000000000000", - "0x72657075746174696f6e00000000000000000000000000000000000000000000", - "0x72657075746174696f6e00000000000000000000000000000000000000000000") - .setOpTopic0_1(LogOp.AND) - .setOpTopic0_2(LogOp.OR) - .setOpTopic0_3(LogOp.AND) - .setOpTopic1_2(LogOp.OR) - .setOpTopic1_3(LogOp.OR) - .setOpTopic2_3(LogOp.OR) - .build(); - - assertNotNull(quadro); - assertNotNull(quadro.getParams()); - } - - @Test(expected = LogQueryException.class) - public void quadroIncorrectTopic2() { - LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - null, - "0x72657075746174696f6e00000000000000000000000000000000000000000000", - "0x72657075746174696f6e00000000000000000000000000000000000000000000") - .setOpTopic0_1(LogOp.AND) - .setOpTopic0_2(LogOp.OR) - .setOpTopic0_3(LogOp.AND) - .setOpTopic1_2(LogOp.OR) - .setOpTopic1_3(LogOp.OR) - .setOpTopic2_3(LogOp.OR) - .build(); - - assertNotNull(quadro); - assertNotNull(quadro.getParams()); - } - - @Test(expected = LogQueryException.class) - public void tupleIncorrectTopic2() { - LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - null) - .setOpTopic0_1(LogOp.AND) - .build(); - - assertNotNull(quadro); - assertNotNull(quadro.getParams()); - } - - @Test(expected = LogQueryException.class) - public void tupleIncorrectTopic1() { - LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic(null, - "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") - .setOpTopic0_1(LogOp.AND) - .build(); - - assertNotNull(quadro); - assertNotNull(quadro.getParams()); - } - - @Test(expected = LogQueryException.class) - public void quadroIncorrectOp1() { - LogTopicQuadro topicQuadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - "0x72657075746174696f6e00000000000000000000000000000000000000000000", - "0x72657075746174696f6e00000000000000000000000000000000000000000000"); - - topicQuadro - .setOpTopic0_1(null) - .setOpTopic0_2(LogOp.OR) - .setOpTopic0_3(LogOp.AND) - .setOpTopic1_2(LogOp.OR) - .setOpTopic1_3(LogOp.OR) - .setOpTopic2_3(LogOp.OR) - .build(); - } - - @Test(expected = LogQueryException.class) - public void quadroIncorrectOp2() { - LogTopicQuadro topicQuadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - "0x72657075746174696f6e00000000000000000000000000000000000000000000", - "0x72657075746174696f6e00000000000000000000000000000000000000000000"); - - topicQuadro.setOpTopic0_1(LogOp.AND) - .setOpTopic0_2(null) - .setOpTopic0_3(LogOp.AND) - .setOpTopic1_2(LogOp.OR) - .setOpTopic1_3(LogOp.OR) - .setOpTopic2_3(LogOp.OR) - .build(); - } - - @Test(expected = LogQueryException.class) - public void quadroIncorrectOp3() { - LogTopicQuadro topicQuadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - "0x72657075746174696f6e00000000000000000000000000000000000000000000", - "0x72657075746174696f6e00000000000000000000000000000000000000000000"); - - topicQuadro - .setOpTopic0_1(LogOp.AND) - .setOpTopic0_2(LogOp.OR) - .setOpTopic0_3(null) - .setOpTopic1_2(LogOp.OR) - .setOpTopic1_3(LogOp.OR) - .setOpTopic2_3(LogOp.OR) - .build(); - } - - @Test(expected = LogQueryException.class) - public void quadroInCorrectAgainTopic() { - LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - "0x72657075746174696f6e00000000000000000000000000000000000000000000", - "0x72657075746174696f6e00000000000000000000000000000000000000000000", - null) - .setOpTopic0_1(LogOp.AND) - .setOpTopic0_2(LogOp.OR) - .setOpTopic0_3(LogOp.AND) - .setOpTopic1_2(LogOp.OR) - .setOpTopic1_3(LogOp.OR) - .setOpTopic2_3(LogOp.OR) - .build(); - - assertNotNull(quadro); - assertNotNull(quadro.getParams()); - } - - @Test(expected = LogQueryException.class) - public void quadroInCorrectOp4() { - LogTopicQuadro topicQuadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - "0x72657075746174696f6e00000000000000000000000000000000000000000000", - "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545"); - - topicQuadro - .setOpTopic0_1(LogOp.AND) - .setOpTopic0_2(LogOp.OR) - .setOpTopic0_3(LogOp.AND) - .setOpTopic1_2(null) - .setOpTopic1_3(LogOp.OR) - .setOpTopic2_3(LogOp.OR) - .build(); - } - - @Test(expected = LogQueryException.class) - public void quadroInCorrectOp5() { - LogTopicQuadro topicQuadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - "0x72657075746174696f6e00000000000000000000000000000000000000000000", - "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545"); - - topicQuadro - .setOpTopic0_1(LogOp.AND) - .setOpTopic0_2(LogOp.OR) - .setOpTopic0_3(LogOp.AND) - .setOpTopic1_2(LogOp.AND) - .setOpTopic1_3(null) - .setOpTopic2_3(LogOp.OR) - .build(); - } - - @Test(expected = LogQueryException.class) - public void quadroInCorrectOp6() { - LogTopicQuadro topicQuadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - "0x72657075746174696f6e00000000000000000000000000000000000000000000", - "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545"); - - topicQuadro - .setOpTopic0_1(LogOp.AND) - .setOpTopic0_2(LogOp.OR) - .setOpTopic0_3(LogOp.AND) - .setOpTopic1_2(LogOp.AND) - .setOpTopic1_3(LogOp.OR) - .setOpTopic2_3(null) - .build(); - } - - @Test(expected = LogQueryException.class) - public void quadroInCorrectTopic() { - LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - "0x72657075746174696f6e00000000000000000000000000000000000000000000", - "", - "") - .setOpTopic0_1(LogOp.AND) - .setOpTopic0_2(LogOp.OR) - .setOpTopic0_3(LogOp.AND) - .setOpTopic1_2(LogOp.OR) - .setOpTopic1_3(LogOp.OR) - .setOpTopic2_3(LogOp.OR) - .build(); - - assertNotNull(quadro); - assertNotNull(quadro.getParams()); - } -} diff --git a/src/test/java/io/api/etherscan/logs/LogsApiTest.java b/src/test/java/io/api/etherscan/logs/LogsApiTest.java deleted file mode 100644 index 7143a83..0000000 --- a/src/test/java/io/api/etherscan/logs/LogsApiTest.java +++ /dev/null @@ -1,91 +0,0 @@ -package io.api.etherscan.logs; - -import io.api.ApiRunner; -import io.api.etherscan.model.Log; -import io.api.etherscan.model.query.LogOp; -import io.api.etherscan.model.query.impl.LogQuery; -import io.api.etherscan.model.query.impl.LogQueryBuilder; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 03.11.2018 - */ -@RunWith(Parameterized.class) -public class LogsApiTest extends ApiRunner { - - private final LogQuery query; - private final int logsSize; - - public LogsApiTest(LogQuery query, int logsSize) { - this.query = query; - this.logsSize = logsSize; - } - - @Parameters - public static Collection data() { - LogQuery single = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") - .build(); - - LogQuery singleInvalidAddr = LogQueryBuilder.with("0x13990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") - .build(); - - LogQuery tupleAnd = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224) - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - "0x72657075746174696f6e00000000000000000000000000000000000000000000") - .setOpTopic0_1(LogOp.AND) - .build(); - - LogQuery tupleOr = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - "0x72657075746174696f6e00000000000000000000000000000000000000000000") - .setOpTopic0_1(LogOp.OR) - .build(); - - return Arrays.asList(new Object[][] { - { single, 423 }, - { singleInvalidAddr, 0 }, - { tupleAnd, 1 }, - { tupleOr, 425 } - }); - } - - @Test - public void validateQuery() { - List logs = getApi().logs().logs(query); - assertEquals(logsSize, logs.size()); - - if (logsSize > 0) { - if (logsSize > 1) { - assertNotEquals(logs.get(0), logs.get(1)); - assertNotEquals(logs.get(0).hashCode(), logs.get(1).hashCode()); - } - - assertNotNull(logs.get(0).getAddress()); - assertNotNull(logs.get(0).getBlockNumber()); - assertNotNull(logs.get(0).getData()); - assertNotNull(logs.get(0).getTimeStamp()); - assertNotNull(logs.get(0).getTransactionHash()); - assertNotNull(logs.get(0).getTransactionIndex()); - assertNotNull(logs.get(0).getGasUsed()); - assertNotNull(logs.get(0).getTopics()); - assertNotNull(logs.get(0).getLogIndex()); - assertNotNull(logs.get(0).getGasPrice()); - assertNotNull(logs.get(0).toString()); - - assertEquals(logs.get(0), logs.get(0)); - assertEquals(logs.get(0).hashCode(), logs.get(0).hashCode()); - } - } -} diff --git a/src/test/java/io/api/etherscan/proxy/ProxyBlockLastNoApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyBlockLastNoApiTest.java deleted file mode 100644 index 5485391..0000000 --- a/src/test/java/io/api/etherscan/proxy/ProxyBlockLastNoApiTest.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.api.etherscan.proxy; - -import io.api.ApiRunner; -import org.junit.Test; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 13.11.2018 - */ -public class ProxyBlockLastNoApiTest extends ApiRunner { - - @Test - public void correct() { - long noLast = getApi().proxy().blockNoLast(); - assertNotEquals(0, noLast); - } -} diff --git a/src/test/java/io/api/etherscan/proxy/ProxyCallApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyCallApiTest.java deleted file mode 100644 index 07d26bd..0000000 --- a/src/test/java/io/api/etherscan/proxy/ProxyCallApiTest.java +++ /dev/null @@ -1,46 +0,0 @@ -package io.api.etherscan.proxy; - -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidAddressException; -import io.api.etherscan.error.InvalidDataHexException; -import io.api.etherscan.util.BasicUtils; -import org.junit.Test; - -import java.util.Optional; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 03.11.2018 - */ -public class ProxyCallApiTest extends ApiRunner { - - @Test - public void correct() { - Optional call = getApi().proxy().call("0xAEEF46DB4855E25702F8237E8f403FddcaF931C0", - "0x70a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724"); - assertTrue(call.isPresent()); - assertFalse(BasicUtils.isNotHex(call.get())); - } - - @Test(expected = InvalidAddressException.class) - public void invalidParamWithError() { - Optional call = getApi().proxy().call("0xEEF46DB4855E25702F8237E8f403FddcaF931C0", - "0x70a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724"); - } - - @Test(expected = InvalidDataHexException.class) - public void invalidParamNotHex() { - Optional call = getApi().proxy().call("0xAEEF46DB4855E25702F8237E8f403FddcaF931C0", - "7-0a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724"); - } - - @Test - public void correctParamWithEmptyExpectedResult() { - Optional call = getApi().proxy().call("0xAEEF16DB4855E25702F8237E8f403FddcaF931C0", - "0x70a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724"); - assertTrue(call.isPresent()); - assertFalse(call.get(), BasicUtils.isNotHex(call.get())); - } -} diff --git a/src/test/java/io/api/etherscan/proxy/ProxyCodeApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyCodeApiTest.java deleted file mode 100644 index 9e4910c..0000000 --- a/src/test/java/io/api/etherscan/proxy/ProxyCodeApiTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.api.etherscan.proxy; - -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidAddressException; -import io.api.etherscan.util.BasicUtils; -import org.junit.Test; - -import java.util.Optional; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 03.11.2018 - */ -public class ProxyCodeApiTest extends ApiRunner { - - @Test - public void correct() { - Optional call = getApi().proxy().code("0xf75e354c5edc8efed9b59ee9f67a80845ade7d0c"); - assertTrue(call.isPresent()); - assertFalse(call.get(), BasicUtils.isNotHex(call.get())); - } - - @Test(expected = InvalidAddressException.class) - public void invalidParamWithError() { - getApi().proxy().code("0f75e354c5edc8efed9b59ee9f67a80845ade7d0c"); - } - - @Test - public void correctParamWithEmptyExpectedResult() { - Optional call = getApi().proxy().code("0xf15e354c5edc8efed9b59ee9f67a80845ade7d0c"); - assertTrue(call.isPresent()); - assertFalse(call.get(), BasicUtils.isNotHex(call.get())); - } -} diff --git a/src/test/java/io/api/etherscan/proxy/ProxyGasApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyGasApiTest.java deleted file mode 100644 index 63e476c..0000000 --- a/src/test/java/io/api/etherscan/proxy/ProxyGasApiTest.java +++ /dev/null @@ -1,46 +0,0 @@ -package io.api.etherscan.proxy; - -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidDataHexException; -import org.junit.Test; - -import java.math.BigInteger; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 03.11.2018 - */ -public class ProxyGasApiTest extends ApiRunner { - - @Test - public void correctPrice() { - BigInteger price = getApi().proxy().gasPrice(); - assertNotNull(price); - assertNotEquals(0, price.intValue()); - } - - @Test - public void correctEstimated() { - BigInteger price = getApi().proxy().gasEstimated(); - assertNotNull(price); - assertNotEquals(0, price.intValue()); - } - - @Test - public void correctEstimatedWithData() { - String dataCustom = "606060405260728060106000396000f360606040526000606060405260728060106000396000f360606040526000"; - BigInteger price = getApi().proxy().gasEstimated(); - BigInteger priceCustom = getApi().proxy().gasEstimated(dataCustom); - assertNotNull(price); - assertNotNull(priceCustom); - assertNotEquals(price, priceCustom); - } - - @Test(expected = InvalidDataHexException.class) - public void invalidParamWithError() { - String dataCustom = "280&60106000396000f360606040526000"; - BigInteger priceCustom = getApi().proxy().gasEstimated(dataCustom); - } -} diff --git a/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java deleted file mode 100644 index 19945e2..0000000 --- a/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.api.etherscan.proxy; - -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidAddressException; -import org.junit.Test; - -import java.util.Optional; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 03.11.2018 - */ -public class ProxyStorageApiTest extends ApiRunner { - - @Test - public void correct() { - Optional call = getApi().proxy().storageAt("0x6e03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 0); - assertFalse(call.isPresent()); - } - - @Test(expected = InvalidAddressException.class) - public void invalidParamWithError() { - getApi().proxy().storageAt("0xe03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 0); - } - - @Test - public void correctParamWithEmptyExpectedResult() { - final Optional call = getApi().proxy().storageAt("0x6e03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 10000); - assertFalse(call.isPresent()); - } -} diff --git a/src/test/java/io/api/etherscan/proxy/ProxyTxCountApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyTxCountApiTest.java deleted file mode 100644 index b81926f..0000000 --- a/src/test/java/io/api/etherscan/proxy/ProxyTxCountApiTest.java +++ /dev/null @@ -1,43 +0,0 @@ -package io.api.etherscan.proxy; - -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidAddressException; -import org.junit.Test; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 03.11.2018 - */ -public class ProxyTxCountApiTest extends ApiRunner { - - @Test - public void correctSended() { - int count = getApi().proxy().txSendCount("0x2910543af39aba0cd09dbb2d50200b3e800a63d2"); - assertNotEquals(0, count); - } - - @Test - public void correctByBlockNo() { - int count = getApi().proxy().txCount(6137420); - assertNotEquals(0, count); - } - - @Test(expected = InvalidAddressException.class) - public void invalidParamWithError() { - int count = getApi().proxy().txSendCount("0xe03d9cce9d60f3e9f2597e13cd4c54c55330cfd"); - } - - @Test - public void correctParamWithEmptyExpectedResultBlockNoExist() { - int count = getApi().proxy().txCount(99999999999L); - assertNotEquals(1, count); - } - - @Test - public void correctParamWithEmptyExpectedResult() { - int count = getApi().proxy().txSendCount("0x1e03d9cce9d60f3e9f2597e13cd4c54c55330cfd"); - assertNotEquals(1, count); - } -} diff --git a/src/test/java/io/api/etherscan/proxy/ProxyTxSendRawApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyTxSendRawApiTest.java deleted file mode 100644 index 40e79a6..0000000 --- a/src/test/java/io/api/etherscan/proxy/ProxyTxSendRawApiTest.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.api.etherscan.proxy; - -import io.api.ApiRunner; -import io.api.etherscan.error.EtherScanException; -import io.api.etherscan.error.InvalidDataHexException; -import org.junit.Test; - -import java.util.Optional; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 03.11.2018 - */ -// TODO contact etherscan and ask about method behavior -public class ProxyTxSendRawApiTest extends ApiRunner { - - public void correct() { - Optional sendRaw = getApi().proxy() - .txSendRaw("0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); - assertTrue(sendRaw.isPresent()); - } - - @Test(expected = InvalidDataHexException.class) - public void invalidParamWithError() { - Optional sendRaw = getApi().proxy().txSendRaw("5151=0561"); - } - - @Test(expected = EtherScanException.class) - public void invalidParamEtherScanDataException() { - Optional sendRaw = getApi().proxy().txSendRaw("0x1"); - } - - public void correctParamWithEmptyExpectedResult() { - Optional sendRaw = getApi().proxy().txSendRaw("0x000000"); - assertFalse(sendRaw.isPresent()); - } -} diff --git a/src/test/java/io/api/etherscan/statistic/StatisticPriceApiTest.java b/src/test/java/io/api/etherscan/statistic/StatisticPriceApiTest.java deleted file mode 100644 index e29a6b1..0000000 --- a/src/test/java/io/api/etherscan/statistic/StatisticPriceApiTest.java +++ /dev/null @@ -1,29 +0,0 @@ -package io.api.etherscan.statistic; - -import io.api.ApiRunner; -import io.api.etherscan.model.Price; -import org.junit.Test; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 03.11.2018 - */ -public class StatisticPriceApiTest extends ApiRunner { - - @Test - public void correct() { - Price price = getApi().stats().lastPrice(); - assertNotNull(price); - assertNotNull(price.btcTimestamp()); - assertNotNull(price.usdTimestamp()); - assertNotEquals(0.0, price.inBtc()); - assertNotEquals(0.0, price.inUsd()); - assertNotNull(price.toString()); - - Price empty = new Price(); - assertNotEquals(price, empty); - assertNotEquals(price.hashCode(), empty.hashCode()); - } -} diff --git a/src/test/java/io/api/etherscan/statistic/StatisticTokenSupplyApiTest.java b/src/test/java/io/api/etherscan/statistic/StatisticTokenSupplyApiTest.java deleted file mode 100644 index 0a84d01..0000000 --- a/src/test/java/io/api/etherscan/statistic/StatisticTokenSupplyApiTest.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.api.etherscan.statistic; - -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidAddressException; -import org.junit.Test; - -import java.math.BigInteger; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 03.11.2018 - */ -public class StatisticTokenSupplyApiTest extends ApiRunner { - - @Test - public void correct() { - BigInteger supply = getApi().stats().supply("0x57d90b64a1a57749b0f932f1a3395792e12e7055"); - assertNotNull(supply); - assertNotEquals(BigInteger.ZERO, supply); - } - - @Test(expected = InvalidAddressException.class) - public void invalidParamWithError() { - BigInteger supply = getApi().stats().supply("0x7d90b64a1a57749b0f932f1a3395792e12e7055"); - } - - @Test - public void correctParamWithEmptyExpectedResult() { - BigInteger supply = getApi().stats().supply("0x51d90b64a1a57749b0f932f1a3395792e12e7055"); - assertNotNull(supply); - assertEquals(0, supply.intValue()); - } -} diff --git a/src/test/java/io/api/etherscan/transaction/TransactionExecApiTest.java b/src/test/java/io/api/etherscan/transaction/TransactionExecApiTest.java deleted file mode 100644 index 25320cc..0000000 --- a/src/test/java/io/api/etherscan/transaction/TransactionExecApiTest.java +++ /dev/null @@ -1,42 +0,0 @@ -package io.api.etherscan.transaction; - -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidTxHashException; -import io.api.etherscan.model.Status; -import org.junit.Test; - -import java.util.Optional; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 03.11.2018 - */ -public class TransactionExecApiTest extends ApiRunner { - - @Test - public void correct() { - Optional status = getApi().txs().execStatus("0x15f8e5ea1079d9a0bb04a4c58ae5fe7654b5b2b4463375ff7ffb490aa0032f3a"); - assertTrue(status.isPresent()); - assertTrue(status.get().haveError()); - assertNotNull(status.get().getErrDescription()); - assertNotNull(status.get().toString()); - - Status empty = new Status(); - assertNotEquals(empty, status.get()); - assertNotEquals(empty.hashCode(), status.get().hashCode()); - } - - @Test(expected = InvalidTxHashException.class) - public void invalidParamWithError() { - getApi().txs().execStatus("0xb513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b"); - } - - @Test - public void correctParamWithEmptyExpectedResult() { - Optional status = getApi().txs().execStatus("0x55f8e5ea1079d9a0bb04a4c58ae5fe7654b5b2b4463375ff7ffb490aa0032f3a"); - assertTrue(status.isPresent()); - assertFalse(status.get().haveError()); - } -} diff --git a/src/test/java/io/api/etherscan/transaction/TransactionReceiptApiTest.java b/src/test/java/io/api/etherscan/transaction/TransactionReceiptApiTest.java deleted file mode 100644 index a459355..0000000 --- a/src/test/java/io/api/etherscan/transaction/TransactionReceiptApiTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package io.api.etherscan.transaction; - -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidTxHashException; -import org.junit.Test; - -import java.util.Optional; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 03.11.2018 - */ -public class TransactionReceiptApiTest extends ApiRunner { - - @Test - public void correct() { - Optional status = getApi().txs() - .receiptStatus("0x513c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76"); - assertTrue(status.isPresent()); - assertTrue(status.get()); - } - - @Test(expected = InvalidTxHashException.class) - public void invalidParamWithError() { - getApi().txs().receiptStatus("0x13c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76"); - } - - @Test - public void correctParamWithEmptyExpectedResult() { - Optional status = getApi().txs() - .receiptStatus("0x113c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76"); - assertFalse(status.isPresent()); - } -} diff --git a/src/test/java/io/api/manager/QueueManagerTest.java b/src/test/java/io/api/manager/QueueManagerTest.java deleted file mode 100644 index 74e674c..0000000 --- a/src/test/java/io/api/manager/QueueManagerTest.java +++ /dev/null @@ -1,54 +0,0 @@ -package io.api.manager; - -import io.api.ApiRunner; -import io.api.etherscan.manager.IQueueManager; -import io.api.etherscan.manager.impl.FakeQueueManager; -import io.api.etherscan.manager.impl.QueueManager; -import org.junit.Test; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 03.11.2018 - */ -public class QueueManagerTest extends ApiRunner { - - @Test - public void fakeManager() { - IQueueManager fakeManager = new FakeQueueManager(); - fakeManager.takeTurn(); - fakeManager.takeTurn(); - fakeManager.takeTurn(); - fakeManager.takeTurn(); - fakeManager.takeTurn(); - fakeManager.takeTurn(); - assertNotNull(fakeManager); - } - - @Test(timeout = 3500) - public void queueManager() { - IQueueManager queueManager = new QueueManager(1, 3); - queueManager.takeTurn(); - queueManager.takeTurn(); - assertNotNull(queueManager); - } - - @Test(timeout = 4500) - public void queueManagerWithDelay() { - IQueueManager queueManager = new QueueManager(1, 2, 2); - queueManager.takeTurn(); - queueManager.takeTurn(); - assertNotNull(queueManager); - } - - @Test - public void queueManagerTimeout() { - IQueueManager queueManager = new QueueManager(1, 3); - queueManager.takeTurn(); - long start = System.currentTimeMillis(); - queueManager.takeTurn(); - long end = System.currentTimeMillis(); - assertEquals(3, Math.round((double) (end - start) / 1000)); - } -} diff --git a/src/test/java/io/api/util/BasicUtilsTests.java b/src/test/java/io/api/util/BasicUtilsTests.java deleted file mode 100644 index c35bada..0000000 --- a/src/test/java/io/api/util/BasicUtilsTests.java +++ /dev/null @@ -1,103 +0,0 @@ -package io.api.util; - -import com.google.gson.Gson; -import io.api.ApiRunner; -import io.api.etherscan.error.EtherScanException; -import io.api.etherscan.error.ParseException; -import io.api.etherscan.model.utility.StringResponseTO; -import org.junit.Test; - -import java.util.ArrayList; -import java.util.List; - -import static io.api.etherscan.util.BasicUtils.*; - -/** - * ! NO DESCRIPTION ! - * - * @author GoodforGod - * @since 13.11.2018 - */ -public class BasicUtilsTests extends ApiRunner { - - @Test(expected = EtherScanException.class) - public void responseValidateEmpty() { - String response = "{\"status\":\"0\",\"message\":\"No ether\",\"result\":\"status\"}"; - StringResponseTO responseTO = new Gson().fromJson(response, StringResponseTO.class); - validateTxResponse(responseTO); - } - - @Test - public void partitionEmpty() { - ArrayList list = new ArrayList<>(); - List> lists = partition(list, 12); - assertTrue(lists.isEmpty()); - } - - @Test - public void partitionNullParam() { - List> lists = partition(null, 12); - assertTrue(lists.isEmpty()); - } - - @Test - public void isBlankNull() { - boolean result = isBlank(null); - assertTrue(result); - } - - @Test - public void isEmptyCollectionNull() { - List list = null; - boolean result = isEmpty(list); - assertTrue(result); - } - - @Test - public void isEmptyCollectionEmpty() { - ArrayList list = new ArrayList<>(); - boolean result = isEmpty(list); - assertTrue(result); - } - - @Test - public void isNotAddressNull() { - boolean result = isNotAddress(""); - assertTrue(result); - } - - @Test - public void isNotHexNull() { - boolean result = isNotHex(""); - assertTrue(result); - } - - @Test - public void isNotAddressInvalid() { - boolean result = isNotAddress("125125"); - assertTrue(result); - } - - @Test - public void isNotHexInvalid() { - boolean result = isNotHex("1215%"); - assertTrue(result); - } - - @Test(expected = EtherScanException.class) - public void isResponseStatusInvalidThrows() { - StringResponseTO responseTO = new StringResponseTO(); - validateTxResponse(responseTO); - } - - @Test(expected = EtherScanException.class) - public void isResponseNullThrows() { - StringResponseTO responseTO = null; - validateTxResponse(responseTO); - } - - @Test(expected = ParseException.class) - public void isThrowParseException() { - throw new ParseException("Test", null, null); - } -} diff --git a/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java b/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java new file mode 100644 index 0000000..4b52c00 --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java @@ -0,0 +1,41 @@ +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import java.util.Map; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; + +public class ApiRunner extends Assertions { + + private static final String DEFAULT_KEY = "YourApiKeyToken"; + + private static final String API_KEY; + private static final EtherScanAPI API; + + static { + API_KEY = System.getenv().entrySet().stream() + .filter(e -> e.getKey().startsWith("ETHERSCAN_API_KEY")) + .map(Map.Entry::getValue) + .findFirst() + .orElse(DEFAULT_KEY); + + final RequestQueueManager queueManager = (DEFAULT_KEY.equals(API_KEY)) + ? RequestQueueManager.anonymous() + : RequestQueueManager.planFree(); + + API = EtherScanAPI.builder() + .withApiKey(ApiRunner.API_KEY) + .withNetwork(EthNetworks.MAINNET) + .withQueue(queueManager) + .build(); + } + + public static EtherScanAPI getApi() { + return API; + } + + @AfterAll + public static void cleanup() throws Exception { + API.close(); + } +} diff --git a/src/test/java/io/goodforgod/api/etherscan/EtherScanAPITests.java b/src/test/java/io/goodforgod/api/etherscan/EtherScanAPITests.java new file mode 100644 index 0000000..36e23ec --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/EtherScanAPITests.java @@ -0,0 +1,78 @@ +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.error.EtherScanConnectionException; +import io.goodforgod.api.etherscan.error.EtherScanKeyException; +import io.goodforgod.api.etherscan.http.EthHttpClient; +import io.goodforgod.api.etherscan.http.impl.UrlEthHttpClient; +import io.goodforgod.api.etherscan.model.Balance; +import java.net.URI; +import java.time.Duration; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; +import org.junit.jupiter.api.Test; + +/** + * @author GoodforGod + * @since 05.11.2018 + */ +class EtherScanAPITests extends ApiRunner { + + private final EthNetworks network = EthNetworks.SEPOLIA; + + @Test + void validKey() { + String validKey = "YourKey"; + EtherScanAPI api = EtherScanAPI.builder().withApiKey(validKey).withNetwork(network).build(); + assertNotNull(api); + } + + @Test + void emptyKey() { + assertThrows(EtherScanKeyException.class, () -> EtherScanAPI.builder().withApiKey("").build()); + } + + @Test + void blankKey() { + assertThrows(EtherScanKeyException.class, + () -> EtherScanAPI.builder().withApiKey(" ").withNetwork(network).build()); + } + + @Test + void noTimeoutOnRead() { + Supplier supplier = () -> new UrlEthHttpClient(Duration.ofMillis(300)); + EtherScanAPI api = EtherScanAPI.builder().withNetwork(EthNetworks.MAINNET).withHttpClient(supplier).build(); + Balance balance = api.account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); + assertNotNull(balance); + } + + @Test + void noTimeoutOnReadGroli() { + Balance balance = getApi().account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); + assertNotNull(balance); + } + + @Test + void noTimeoutOnReadTobalala() { + Balance balance = getApi().account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); + assertNotNull(balance); + } + + @Test + void noTimeoutUnlimitedAwait() { + Balance balance = getApi().account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); + assertNotNull(balance); + } + + @Test + void timeout() throws InterruptedException { + TimeUnit.SECONDS.sleep(5); + Supplier supplier = () -> new UrlEthHttpClient(Duration.ofMillis(300), Duration.ofMillis(300)); + EtherScanAPI api = EtherScanAPI.builder() + .withNetwork(() -> URI.create("https://api-unknown.etherscan.io/api")) + .withHttpClient(supplier) + .build(); + + assertThrows(EtherScanConnectionException.class, + () -> api.account().blocksMined("0x0010f94b296A852aAac52EA6c5Ac72e03afD032D")); + } +} diff --git a/src/test/java/io/api/etherscan/account/AccountBalanceListTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceListTests.java similarity index 69% rename from src/test/java/io/api/etherscan/account/AccountBalanceListTest.java rename to src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceListTests.java index fdeb1e9..0054a84 100644 --- a/src/test/java/io/api/etherscan/account/AccountBalanceListTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceListTests.java @@ -1,25 +1,22 @@ -package io.api.etherscan.account; - -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidAddressException; -import io.api.etherscan.model.Balance; -import io.api.support.AddressUtil; -import org.junit.Test; +package io.goodforgod.api.etherscan.account; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.model.Balance; +import io.goodforgod.api.etherscan.support.AddressUtil; import java.math.BigInteger; import java.util.ArrayList; import java.util.List; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class AccountBalanceListTest extends ApiRunner { +class AccountBalanceListTests extends ApiRunner { @Test - public void correct() { + void correct() { List addresses = new ArrayList<>(); addresses.add("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9"); addresses.add("0xC9F32CE1127e44C51cbD182D6364F3D707Fd0d47"); @@ -32,19 +29,15 @@ public void correct() { assertNotEquals(balances.get(0).hashCode(), balances.get(1).hashCode()); for (Balance balance : balances) { assertNotNull(balance.getAddress()); - assertNotNull(balance.getGwei()); - assertNotNull(balance.getKwei()); - assertNotNull(balance.getMwei()); - assertNotNull(balance.getEther()); - assertNotNull(balance.getGwei()); + assertNotNull(balance.getBalanceInWei()); assertNotNull(balance.getAddress()); - assertNotEquals(BigInteger.ZERO, balance.getWei()); + assertNotEquals(BigInteger.ZERO, balance.getBalanceInWei().asWei()); assertNotNull(balance.toString()); } } @Test - public void correctMoreThat20Addresses() { + void correctMoreThat20Addresses() { List addresses = AddressUtil.genRealAddresses(); List balances = getApi().account().balances(addresses); @@ -58,17 +51,17 @@ public void correctMoreThat20Addresses() { assertNotEquals(balances.get(0), balances.get(1)); } - @Test(expected = InvalidAddressException.class) - public void invalidParamWithError() { + @Test + void invalidParamWithError() { List addresses = new ArrayList<>(); addresses.add("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9"); addresses.add("C9F32CE1127e44C51cbD182D6364F3D707Fd0d47"); - getApi().account().balances(addresses); + assertThrows(EtherScanInvalidAddressException.class, () -> getApi().account().balances(addresses)); } @Test - public void emptyParamList() { + void emptyParamList() { List addresses = new ArrayList<>(); List balances = getApi().account().balances(addresses); assertNotNull(balances); @@ -76,7 +69,7 @@ public void emptyParamList() { } @Test - public void correctParamWithEmptyExpectedResult() { + void correctParamWithEmptyExpectedResult() { List addresses = new ArrayList<>(); addresses.add("0x1327cb34984c3992ec1EA0eAE98Ccf80A74f95B9"); addresses.add("0xC1F32CE1127e44C51cbD182D6364F3D707Fd0d47"); @@ -87,7 +80,7 @@ public void correctParamWithEmptyExpectedResult() { assertEquals(2, balances.size()); for (Balance balance : balances) { assertNotNull(balance.getAddress()); - assertEquals(0, balance.getWei().intValue()); + assertEquals(0, balance.getBalanceInWei().asWei().intValue()); } } } diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceTests.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceTests.java new file mode 100644 index 0000000..ed537c6 --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceTests.java @@ -0,0 +1,40 @@ +package io.goodforgod.api.etherscan.account; + +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.EtherScanAPI; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.model.Balance; +import org.junit.jupiter.api.Test; + +/** + * @author GoodforGod + * @since 03.11.2018 + */ +class AccountBalanceTests extends ApiRunner { + + private final EtherScanAPI api = getApi(); + + @Test + void correct() { + Balance balance = api.account().balance("0x8d4426f94e42f721C7116E81d6688cd935cB3b4F"); + assertNotNull(balance); + assertNotNull(balance.getBalanceInWei()); + assertNotNull(balance.getAddress()); + assertNotNull(balance.toString()); + } + + @Test + void invalidParamWithError() { + assertThrows(EtherScanInvalidAddressException.class, + () -> getApi().account().balance("8d4426f94e42f721C7116E81d6688cd935cB3b4F")); + } + + @Test + void correctParamWithEmptyExpectedResult() { + Balance balance = api.account().balance("0x1d4426f94e42f721C7116E81d6688cd935cB3b4F"); + assertNotNull(balance); + assertNotNull(balance.getBalanceInWei()); + assertNotNull(balance.getAddress()); + assertEquals(0, balance.getBalanceInWei().asWei().intValue()); + } +} diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountMinedBlocksTests.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountMinedBlocksTests.java new file mode 100644 index 0000000..3e19e96 --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountMinedBlocksTests.java @@ -0,0 +1,53 @@ +package io.goodforgod.api.etherscan.account; + +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.EtherScanAPI; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.model.Block; +import java.util.List; +import org.junit.jupiter.api.Test; + +/** + * @author GoodforGod + * @since 03.11.2018 + */ +class AccountMinedBlocksTests extends ApiRunner { + + private final EtherScanAPI api = getApi(); + + @Test + void correct() { + List blocks = api.account().blocksMined("0xE4C6175183029A0f039bf2DFffa5C6e8F3cA9B23"); + assertNotNull(blocks); + + assertEquals(223, blocks.size()); + assertBlocks(blocks); + assertNotNull(blocks.get(0).toString()); + + if (blocks.size() > 1) { + assertNotEquals(blocks.get(0), blocks.get(1)); + assertNotEquals(blocks.get(0).hashCode(), blocks.get(1).hashCode()); + } + } + + @Test + void invalidParamWithError() { + assertThrows(EtherScanInvalidAddressException.class, + () -> getApi().account().blocksMined("xE4C6175183029A0f039bf2DFffa5C6e8F3cA9B23")); + } + + @Test + void correctParamWithEmptyExpectedResult() { + List txs = api.account().blocksMined("0xE1C6175183029A0f039bf2DFffa5C6e8F3cA9B23"); + assertNotNull(txs); + assertTrue(txs.isEmpty()); + } + + private void assertBlocks(List blocks) { + for (Block block : blocks) { + assertNotEquals(0, block.getBlockNumber()); + assertNotNull(block.getBlockReward()); + assertNotNull(block.getTimeStamp()); + } + } +} diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountTokenBalanceTests.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTokenBalanceTests.java new file mode 100644 index 0000000..3919982 --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTokenBalanceTests.java @@ -0,0 +1,56 @@ +package io.goodforgod.api.etherscan.account; + +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.EtherScanAPI; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.model.TokenBalance; +import org.junit.jupiter.api.Test; + +/** + * @author GoodforGod + * @since 03.11.2018 + */ +class AccountTokenBalanceTests extends ApiRunner { + + private final EtherScanAPI api = getApi(); + + @Test + void correct() { + TokenBalance balance = api.account().balance("0x5d807e7F124EC2103a59c5249187f772c0b8D6b2", + "0x5EaC95ad5b287cF44E058dCf694419333b796123"); + assertNotNull(balance); + assertNotNull(balance.getBalanceInWei()); + assertNotNull(balance.getAddress()); + assertNotNull(balance.getContract()); + assertNotNull(balance.toString()); + + TokenBalance balance2 = new TokenBalance("125161", balance.getBalanceInWei(), balance.getContract()); + assertNotEquals(balance, balance2); + assertNotEquals(balance.hashCode(), balance2.hashCode()); + } + + @Test + void invalidAddressParamWithError() { + assertThrows(EtherScanInvalidAddressException.class, + () -> api.account().balance("0x5807e7F124EC2103a59c5249187f772c0b8D6b2", + "0x5EaC95ad5b287cF44E058dCf694419333b796123")); + } + + @Test + void invalidContractParamWithError() { + assertThrows(EtherScanInvalidAddressException.class, + () -> api.account().balance("0x5d807e7F124EC2103a59c5249187f772c0b8D6b2", + "0xEaC95ad5b287cF44E058dCf694419333b796123")); + } + + @Test + void correctParamWithEmptyExpectedResult() { + TokenBalance balance = api.account().balance("0x1d807e7F124EC2103a59c5249187f772c0b8D6b2", + "0x5EaC95ad5b287cF44E058dCf694419333b796123"); + assertNotNull(balance); + assertNotNull(balance.getBalanceInWei()); + assertNotNull(balance.getAddress()); + assertNotNull(balance.getContract()); + assertEquals(0, balance.getBalanceInWei().asWei().intValue()); + } +} diff --git a/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxErc20Tests.java similarity index 58% rename from src/test/java/io/api/etherscan/account/AccountTxTokenTest.java rename to src/test/java/io/goodforgod/api/etherscan/account/AccountTxErc20Tests.java index b82d4d1..4239bcd 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxErc20Tests.java @@ -1,27 +1,24 @@ -package io.api.etherscan.account; - -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidAddressException; -import io.api.etherscan.model.TxToken; -import org.junit.Test; +package io.goodforgod.api.etherscan.account; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.model.TxErc20; import java.util.List; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class AccountTxTokenTest extends ApiRunner { +class AccountTxErc20Tests extends ApiRunner { @Test - public void correct() { - List txs = getApi().account().txsToken("0xE376F69ED2218076682e2b3B7b9099eC50aD68c4"); + void correct() { + List txs = getApi().account().txsErc20("0xE376F69ED2218076682e2b3B7b9099eC50aD68c4"); assertNotNull(txs); assertEquals(3, txs.size()); assertTxs(txs); - assertNotEquals(0, txs.get(0).getGasPrice()); + assertNotEquals(0, txs.get(0).getGasPrice().asWei().intValue()); assertNotEquals(-1, txs.get(0).getNonce()); assertNotNull(txs.get(0).toString()); @@ -35,35 +32,36 @@ public void correct() { } @Test - public void correctStartBlock() { - List txs = getApi().account().txsToken("0x36ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7", 5578167); + void correctStartBlock() { + List txs = getApi().account().txsErc20("0x36ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7", 5578167); assertNotNull(txs); assertEquals(11, txs.size()); assertTxs(txs); } @Test - public void correctStartBlockEndBlock() { - List txs = getApi().account().txsToken("0x36ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7", 5578167, 5813576); + void correctStartBlockEndBlock() { + List txs = getApi().account().txsErc20("0x36ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7", 5578167, 5813576); assertNotNull(txs); assertEquals(5, txs.size()); assertTxs(txs); } - @Test(expected = InvalidAddressException.class) - public void invalidParamWithError() { - getApi().account().txsToken("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); + @Test + void invalidParamWithError() { + assertThrows(EtherScanInvalidAddressException.class, + () -> getApi().account().txsErc20("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7")); } @Test - public void correctParamWithEmptyExpectedResult() { - List txs = getApi().account().txsToken("0x31ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); + void correctParamWithEmptyExpectedResult() { + List txs = getApi().account().txsErc20("0x31ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); assertNotNull(txs); assertTrue(txs.isEmpty()); } - private void assertTxs(List txs) { - for (TxToken tx : txs) { + private void assertTxs(List txs) { + for (TxErc20 tx : txs) { assertNotNull(tx.getBlockHash()); assertNotNull(tx.getTokenName()); assertNotNull(tx.getTokenSymbol()); @@ -73,7 +71,7 @@ private void assertTxs(List txs) { assertNotNull(tx.getTokenDecimal()); assertNotEquals(-1, (tx.getConfirmations())); assertNotNull(tx.getGasUsed()); - assertNotEquals(-1, tx.getCumulativeGasUsed()); + assertNotEquals(-1, tx.getGasUsedCumulative().asWei().intValue()); assertNotEquals(-1, tx.getTransactionIndex()); } } diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalByHashTests.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalByHashTests.java new file mode 100644 index 0000000..eb06b60 --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalByHashTests.java @@ -0,0 +1,65 @@ +package io.goodforgod.api.etherscan.account; + +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.EtherScanAPI; +import io.goodforgod.api.etherscan.error.EtherScanInvalidTxHashException; +import io.goodforgod.api.etherscan.model.TxInternal; +import io.goodforgod.api.etherscan.util.BasicUtils; +import java.util.List; +import org.junit.jupiter.api.Test; + +/** + * @author GoodforGod + * @since 03.11.2018 + */ +class AccountTxInternalByHashTests extends ApiRunner { + + private final EtherScanAPI api = getApi(); + + @Test + void correct() { + List txs = api.account() + .txsInternalByHash("0x1b513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b"); + assertNotNull(txs); + assertEquals(1, txs.size()); + assertTxs(txs); + assertNotNull(txs.get(0).getFrom()); + assertNotNull(txs.get(0).getTimeStamp()); + assertNotNull(txs.get(0).getGas()); + assertNotNull(txs.get(0).getValue()); + assertNotNull(txs.get(0).getType()); + assertFalse(txs.get(0).haveError()); + assertFalse(txs.get(0).haveError()); + assertNotEquals("-1", txs.get(0).getTraceIdAsString()); + assertTrue(BasicUtils.isEmpty(txs.get(0).getErrCode())); + assertNotNull(txs.get(0).toString()); + + if (txs.size() > 9) { + assertNotEquals(txs.get(0), txs.get(9)); + assertNotEquals(txs.get(0).hashCode(), txs.get(9).hashCode()); + } + } + + @Test + void invalidParamWithError() { + assertThrows(EtherScanInvalidTxHashException.class, + () -> api.account().txsInternalByHash("0xb513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b")); + } + + @Test + void correctParamWithEmptyExpectedResult() { + List txs = api.account() + .txsInternalByHash("0x2b513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b"); + assertNotNull(txs); + assertTrue(txs.isEmpty()); + } + + private void assertTxs(List txs) { + for (TxInternal tx : txs) { + assertNotEquals(0, tx.getBlockNumber()); + assertNotNull(tx.getFrom()); + assertNotNull(tx.getTo()); + assertNotNull(tx.getTimeStamp()); + } + } +} diff --git a/src/test/java/io/api/etherscan/account/AccountTxInternalTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalTests.java similarity index 68% rename from src/test/java/io/api/etherscan/account/AccountTxInternalTest.java rename to src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalTests.java index 47f3e61..1d4220d 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxInternalTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalTests.java @@ -1,22 +1,19 @@ -package io.api.etherscan.account; - -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidAddressException; -import io.api.etherscan.model.TxInternal; -import org.junit.Test; +package io.goodforgod.api.etherscan.account; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.model.TxInternal; import java.util.List; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class AccountTxInternalTest extends ApiRunner { +class AccountTxInternalTests extends ApiRunner { @Test - public void correct() { + void correct() { List txs = getApi().account().txsInternal("0x2C1ba59D6F58433FB1EaEe7d20b26Ed83bDA51A3"); assertNotNull(txs); assertEquals(66, txs.size()); @@ -25,7 +22,7 @@ public void correct() { } @Test - public void correctStartBlock() { + void correctStartBlock() { List txs = getApi().account().txsInternal("0x2C1ba59D6F58433FB1EaEe7d20b26Ed83bDA51A3", 2558775); assertNotNull(txs); assertEquals(24, txs.size()); @@ -35,20 +32,21 @@ public void correctStartBlock() { } @Test - public void correctStartBlockEndBlock() { + void correctStartBlockEndBlock() { List txs = getApi().account().txsInternal("0x2C1ba59D6F58433FB1EaEe7d20b26Ed83bDA51A3", 2558775, 2685504); assertNotNull(txs); assertEquals(21, txs.size()); assertTxs(txs); } - @Test(expected = InvalidAddressException.class) - public void invalidParamWithError() { - List txs = getApi().account().txsInternal("0x2C1ba59D6F58433FB1EaEe7d20b26Ed83bDA51"); + @Test + void invalidParamWithError() { + assertThrows(EtherScanInvalidAddressException.class, + () -> getApi().account().txsInternal("0x2C1ba59D6F58433FB1EaEe7d20b26Ed83bDA51")); } @Test - public void correctParamWithEmptyExpectedResult() { + void correctParamWithEmptyExpectedResult() { List txs = getApi().account().txsInternal("0x2C1ba59D6F58433FB2EaEe7d20b26Ed83bDA51A3"); assertNotNull(txs); assertTrue(txs.isEmpty()); diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc1155TokenTests.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc1155TokenTests.java new file mode 100644 index 0000000..d8cbb73 --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc1155TokenTests.java @@ -0,0 +1,81 @@ +package io.goodforgod.api.etherscan.account; + +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.model.TxErc1155; +import java.util.List; +import org.junit.jupiter.api.Test; + +/** + * @author GoodforGod + * @since 14.05.2023 + */ +class AccountTxRc1155TokenTests extends ApiRunner { + + @Test + void correct() { + List txs = getApi().account().txsErc1155("0xE4C8324534C0C6bCA174Cd0F02fAC9889C36bA59"); + assertNotNull(txs); + assertFalse(txs.isEmpty()); + assertTxs(txs); + assertNotEquals(0, txs.get(0).getGasPrice().asWei().intValue()); + assertNotEquals(-1, txs.get(0).getNonce()); + + assertNotNull(txs.get(0).toString()); + assertNotEquals(txs.get(0).toString(), txs.get(1).toString()); + + assertNotEquals(txs.get(0), txs.get(1)); + assertNotEquals(txs.get(0).hashCode(), txs.get(1).hashCode()); + + assertEquals(txs.get(1), txs.get(1)); + assertEquals(txs.get(1).hashCode(), txs.get(1).hashCode()); + } + + @Test + void correctStartBlock() { + List txs = getApi().account().txsErc1155("0xE4C8324534C0C6bCA174Cd0F02fAC9889C36bA59", 14275897); + assertNotNull(txs); + assertFalse(txs.isEmpty()); + assertTxs(txs); + } + + @Test + void correctStartBlockEndBlock() { + List txs = getApi().account().txsErc1155("0xE4C8324534C0C6bCA174Cd0F02fAC9889C36bA59", 14275897, 15148929); + assertNotNull(txs); + assertEquals(11, txs.size()); + assertTxs(txs); + } + + @Test + void invalidParamWithError() { + assertThrows(EtherScanInvalidAddressException.class, + () -> getApi().account().txsErc1155("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7")); + } + + @Test + void correctParamWithEmptyExpectedResult() { + List txs = getApi().account().txsErc1155("0x31ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); + assertNotNull(txs); + assertTrue(txs.isEmpty()); + } + + private void assertTxs(List txs) { + txs.forEach(this::asserTx); + } + + private void asserTx(TxErc1155 tx) { + assertNotNull(tx.getBlockHash()); + assertNotNull(tx.getTokenName()); + assertNotNull(tx.getTokenSymbol()); + assertNotNull(tx.getFrom()); + assertNotNull(tx.getTo()); + assertNotNull(tx.getTimeStamp()); + assertNotNull(tx.getTokenID()); + assertNotNull(tx.getTokenValue()); + assertNotEquals(-1, (tx.getConfirmations())); + assertNotNull(tx.getGasUsed()); + assertNotEquals(-1, tx.getGasUsedCumulative().asWei().intValue()); + assertNotEquals(-1, tx.getTransactionIndex()); + } +} diff --git a/src/test/java/io/api/etherscan/account/AccountTxRc721TokenTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTests.java similarity index 52% rename from src/test/java/io/api/etherscan/account/AccountTxRc721TokenTest.java rename to src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTests.java index 0afa12f..6c61a4c 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxRc721TokenTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTests.java @@ -1,25 +1,24 @@ -package io.api.etherscan.account; - -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidAddressException; -import io.api.etherscan.model.TxToken; -import org.junit.Test; +package io.goodforgod.api.etherscan.account; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.model.TxErc721; import java.util.List; +import org.junit.jupiter.api.Test; /** * @author NGuggs * @since 11.28.2021 */ -public class AccountTxRc721TokenTest extends ApiRunner { +class AccountTxRc721TokenTests extends ApiRunner { @Test - public void correct() { - List txs = getApi().account().txsNftToken("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67"); + void correct() { + List txs = getApi().account().txsErc721("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67"); assertNotNull(txs); assertEquals(16, txs.size()); assertTxs(txs); - assertNotEquals(0, txs.get(0).getGasPrice()); + assertNotEquals(0, txs.get(0).getGasPrice().asWei().intValue()); assertNotEquals(-1, txs.get(0).getNonce()); assertNotNull(txs.get(0).toString()); @@ -33,8 +32,8 @@ public void correct() { } @Test - public void correctStartBlock() { - List txs = getApi().account().txsNftToken("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67", 4762071); + void correctStartBlock() { + List txs = getApi().account().txsErc721("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67", 4762071); System.out.println(txs); assertNotNull(txs); assertEquals(5, txs.size()); @@ -42,28 +41,29 @@ public void correctStartBlock() { } @Test - public void correctStartBlockEndBlock() { - List txs = getApi().account().txsNftToken("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67", 4761862, 4761934); + void correctStartBlockEndBlock() { + List txs = getApi().account().txsErc721("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67", 4761862, 4761934); System.out.println(txs); assertNotNull(txs); assertEquals(11, txs.size()); assertTxs(txs); } - @Test(expected = InvalidAddressException.class) - public void invalidParamWithError() { - getApi().account().txsNftToken("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); + @Test + void invalidParamWithError() { + assertThrows(EtherScanInvalidAddressException.class, + () -> getApi().account().txsErc721("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7")); } @Test - public void correctParamWithEmptyExpectedResult() { - List txs = getApi().account().txsNftToken("0x31ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); + void correctParamWithEmptyExpectedResult() { + List txs = getApi().account().txsErc721("0x31ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); assertNotNull(txs); assertTrue(txs.isEmpty()); } - private void assertTxs(List txs) { - for (TxToken tx : txs) { + private void assertTxs(List txs) { + for (TxErc721 tx : txs) { assertNotNull(tx.getBlockHash()); assertNotNull(tx.getTokenName()); assertNotNull(tx.getTokenSymbol()); @@ -73,7 +73,7 @@ private void assertTxs(List txs) { assertNotNull(tx.getTokenDecimal()); assertNotEquals(-1, (tx.getConfirmations())); assertNotNull(tx.getGasUsed()); - assertNotEquals(-1, tx.getCumulativeGasUsed()); + assertNotEquals(-1, tx.getGasUsedCumulative().asWei().intValue()); assertNotEquals(-1, tx.getTransactionIndex()); } } diff --git a/src/test/java/io/api/etherscan/account/AccountTxsTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxsTests.java similarity index 72% rename from src/test/java/io/api/etherscan/account/AccountTxsTest.java rename to src/test/java/io/goodforgod/api/etherscan/account/AccountTxsTests.java index 66a95e4..653f62a 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxsTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxsTests.java @@ -1,22 +1,19 @@ -package io.api.etherscan.account; - -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidAddressException; -import io.api.etherscan.model.Tx; -import org.junit.Test; +package io.goodforgod.api.etherscan.account; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.model.Tx; import java.util.List; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class AccountTxsTest extends ApiRunner { +class AccountTxsTests extends ApiRunner { @Test - public void correct() { + void correct() { List txs = getApi().account().txs("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9"); assertNotNull(txs); assertEquals(5, txs.size()); @@ -27,7 +24,7 @@ public void correct() { assertNotNull(txs.get(0).getTo()); assertNotNull(txs.get(0).getBlockHash()); assertNotNull(txs.get(0).getGas()); - assertNotNull(txs.get(0).getCumulativeGasUsed()); + assertNotNull(txs.get(0).getGasUsedCumulative()); assertNotNull(txs.get(0).getGasPrice()); assertNotNull(txs.get(0).getValue()); assertNotNull(txs.get(0).getContractAddress()); @@ -39,7 +36,7 @@ public void correct() { } @Test - public void correctStartBlock() { + void correctStartBlock() { List txs = getApi().account().txs("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9", 3892842); assertNotNull(txs); assertEquals(4, txs.size()); @@ -47,21 +44,22 @@ public void correctStartBlock() { } @Test - public void correctStartBlockEndBlock() { + void correctStartBlockEndBlock() { List txs = getApi().account().txs("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9", 3892842, 3945741); assertNotNull(txs); assertEquals(3, txs.size()); assertTxs(txs); - assertFalse(txs.get(0).equals(txs.get(1))); + assertNotEquals(txs.get(0), txs.get(1)); } - @Test(expected = InvalidAddressException.class) - public void invalidParamWithError() { - List txs = getApi().account().txs("9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9"); + @Test + void invalidParamWithError() { + assertThrows(EtherScanInvalidAddressException.class, + () -> getApi().account().txs("9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9")); } @Test - public void correctParamWithEmptyExpectedResult() { + void correctParamWithEmptyExpectedResult() { List txs = getApi().account().txs("0x9321cb34984c3992ec1EA0eAE98Ccf80A74f95B9"); assertNotNull(txs); assertTrue(txs.isEmpty()); @@ -77,7 +75,7 @@ private void assertTxs(List txs) { assertNotEquals(-1, (tx.getNonce())); assertNotEquals(0, (tx.getTransactionIndex())); assertNotEquals(0, tx.getConfirmations()); - assertNotNull(tx.getTxreceipt_status()); + assertNotNull(tx.getTxReceiptStatus()); } } } diff --git a/src/test/java/io/api/etherscan/block/BlockApiTest.java b/src/test/java/io/goodforgod/api/etherscan/block/BlockApiTests.java similarity index 74% rename from src/test/java/io/api/etherscan/block/BlockApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/block/BlockApiTests.java index 34b9de5..7a923aa 100644 --- a/src/test/java/io/api/etherscan/block/BlockApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/block/BlockApiTests.java @@ -1,22 +1,19 @@ -package io.api.etherscan.block; - -import io.api.ApiRunner; -import io.api.etherscan.model.UncleBlock; -import org.junit.Test; +package io.goodforgod.api.etherscan.block; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.model.BlockUncle; import java.util.Optional; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class BlockApiTest extends ApiRunner { +class BlockApiTests extends ApiRunner { @Test - public void correct() { - Optional uncle = getApi().block().uncles(2165403); + void correct() { + Optional uncle = getApi().block().uncles(2165403); assertTrue(uncle.isPresent()); assertFalse(uncle.get().isEmpty()); assertNotNull(uncle.get().getBlockMiner()); @@ -28,7 +25,7 @@ public void correct() { assertNotEquals(-1, uncle.get().getUncles().get(0).getUnclePosition()); assertNotNull(uncle.get().toString()); - UncleBlock empty = new UncleBlock(); + BlockUncle empty = BlockUncle.builder().build(); assertNotEquals(uncle.get().hashCode(), empty.hashCode()); assertNotEquals(uncle.get(), empty); assertTrue(empty.isEmpty()); @@ -46,15 +43,15 @@ public void correct() { } @Test - public void correctNoUncles() { - Optional uncles = getApi().block().uncles(34); + void correctNoUncles() { + Optional uncles = getApi().block().uncles(34); assertTrue(uncles.isPresent()); assertTrue(uncles.get().getUncles().isEmpty()); } @Test - public void correctParamWithEmptyExpectedResult() { - Optional uncles = getApi().block().uncles(99999999934L); + void correctParamWithEmptyExpectedResult() { + Optional uncles = getApi().block().uncles(99999999934L); assertFalse(uncles.isPresent()); } } diff --git a/src/test/java/io/api/etherscan/contract/ContractApiTest.java b/src/test/java/io/goodforgod/api/etherscan/contract/ContractApiTests.java similarity index 54% rename from src/test/java/io/api/etherscan/contract/ContractApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/contract/ContractApiTests.java index 6b4d7d8..4fd0fdb 100644 --- a/src/test/java/io/api/etherscan/contract/ContractApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/contract/ContractApiTests.java @@ -1,20 +1,18 @@ -package io.api.etherscan.contract; +package io.goodforgod.api.etherscan.contract; -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidAddressException; -import io.api.etherscan.model.Abi; -import org.junit.Test; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.model.Abi; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class ContractApiTest extends ApiRunner { +class ContractApiTests extends ApiRunner { @Test - public void correct() { + void correct() { Abi abi = getApi().contract().contractAbi("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413"); assertNotNull(abi); assertTrue(abi.isVerified()); @@ -27,13 +25,14 @@ public void correct() { assertNotEquals(empty.hashCode(), abi.hashCode()); } - @Test(expected = InvalidAddressException.class) - public void invalidParamWithError() { - getApi().contract().contractAbi("0xBBbc244D798123fDe783fCc1C72d3Bb8C189413"); + @Test + void invalidParamWithError() { + assertThrows(EtherScanInvalidAddressException.class, + () -> getApi().contract().contractAbi("0xBBbc244D798123fDe783fCc1C72d3Bb8C189413")); } @Test - public void correctParamWithEmptyExpectedResult() { + void correctParamWithEmptyExpectedResult() { Abi abi = getApi().contract().contractAbi("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413"); assertNotNull(abi); assertTrue(abi.isVerified()); diff --git a/src/test/java/io/goodforgod/api/etherscan/gastracker/GasTrackerApiTests.java b/src/test/java/io/goodforgod/api/etherscan/gastracker/GasTrackerApiTests.java new file mode 100644 index 0000000..b309dd9 --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/gastracker/GasTrackerApiTests.java @@ -0,0 +1,32 @@ +package io.goodforgod.api.etherscan.gastracker; + +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.model.GasOracle; +import io.goodforgod.api.etherscan.model.Wei; +import java.time.Duration; +import org.junit.jupiter.api.Test; + +/** + * @author GoodforGod + * @since 14.05.2023 + */ +class GasTrackerApiTests extends ApiRunner { + + @Test + void estimate() { + Duration estimate = getApi().gasTracker().estimate(Wei.ofWei(123)); + assertNotNull(estimate); + } + + @Test + void oracle() { + GasOracle oracle = getApi().gasTracker().oracle(); + assertNotNull(oracle); + assertNotNull(oracle.getGasUsedRatio()); + assertNotNull(oracle.getFastGasPriceInWei()); + assertNotNull(oracle.getLastBlock()); + assertNotNull(oracle.getProposeGasPriceInWei()); + assertNotNull(oracle.getSafeGasPriceInWei()); + assertNotNull(oracle.getSuggestBaseFee()); + } +} diff --git a/src/test/java/io/goodforgod/api/etherscan/logs/LogQueryBuilderTests.java b/src/test/java/io/goodforgod/api/etherscan/logs/LogQueryBuilderTests.java new file mode 100644 index 0000000..955443c --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/logs/LogQueryBuilderTests.java @@ -0,0 +1,320 @@ +package io.goodforgod.api.etherscan.logs; + +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.error.EtherScanLogQueryException; +import io.goodforgod.api.etherscan.model.query.*; +import org.junit.jupiter.api.Test; + +/** + * @author GoodforGod + * @since 03.11.2018 + */ +class LogQueryBuilderTests extends ApiRunner { + + @Test + void singleCorrect() { + LogQuery single = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") + .build(); + + assertNotNull(single); + assertNotNull(single.params()); + } + + @Test + void singleInCorrectAddress() { + assertThrows(EtherScanInvalidAddressException.class, + () -> LogQuery.builder("033990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") + .build()); + } + + @Test + void singleInCorrectTopic() { + assertThrows(EtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("6516=") + .build()); + } + + @Test + void tupleCorrect() { + LogQuery tuple = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c").withBlockFrom(379224) + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0x72657075746174696f6e00000000000000000000000000000000000000000000") + .setOpTopic0_1(LogOp.AND) + .build(); + + assertNotNull(tuple); + assertNotNull(tuple.params()); + } + + @Test + void tupleInCorrectOp() { + assertThrows(EtherScanLogQueryException.class, + () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c").withBlockFrom(379224) + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0x72657075746174696f6e00000000000000000000000000000000000000000000") + .setOpTopic0_1(null) + .build()); + } + + @Test + void tripleCorrect() { + LogQuery triple = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c").withBlockFrom(379224).withBlockTo(400000) + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0x72657075746174696f6e00000000000000000000000000000000000000000000", + "0x72657075746174696f6e00000000000000000000000000000000000000000000") + .setOpTopic0_1(LogOp.AND) + .setOpTopic0_2(LogOp.OR) + .setOpTopic1_2(LogOp.AND) + .build(); + + assertNotNull(triple); + assertNotNull(triple.params()); + } + + @Test + void tripleInCorrectOp() { + assertThrows(EtherScanLogQueryException.class, + () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c").withBlockFrom(379224).withBlockTo(400000) + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0x72657075746174696f6e00000000000000000000000000000000000000000000", + "0x72657075746174696f6e00000000000000000000000000000000000000000000") + .setOpTopic0_1(LogOp.AND) + .setOpTopic0_2(null) + .setOpTopic1_2(LogOp.AND) + .build()); + } + + @Test + void tripleInCorrectTopic1() { + assertThrows(EtherScanLogQueryException.class, + () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c").withBlockFrom(379224).withBlockTo(400000) + .withTopic(null, + "0x72657075746174696f6e00000000000000000000000000000000000000000000", + "0x72657075746174696f6e00000000000000000000000000000000000000000000") + .setOpTopic0_1(LogOp.AND) + .setOpTopic0_2(LogOp.AND) + .setOpTopic1_2(LogOp.AND) + .build()); + } + + @Test + void tripleInCorrectTopic2() { + assertThrows(EtherScanLogQueryException.class, + () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c").withBlockFrom(379224).withBlockTo(400000) + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + null, + "0x72657075746174696f6e00000000000000000000000000000000000000000000") + .setOpTopic0_1(LogOp.AND) + .setOpTopic0_2(LogOp.AND) + .setOpTopic1_2(LogOp.AND) + .build()); + } + + @Test + void tripleInCorrectTopic3() { + assertThrows(EtherScanLogQueryException.class, + () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c").withBlockFrom(379224).withBlockTo(400000) + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0x72657075746174696f6e00000000000000000000000000000000000000000000", + null) + .setOpTopic0_1(LogOp.AND) + .setOpTopic0_2(LogOp.AND) + .setOpTopic1_2(LogOp.AND) + .build()); + } + + @Test + void quadroCorrect() { + LogQuery quadro = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0x72657075746174696f6e00000000000000000000000000000000000000000000", + "0x72657075746174696f6e00000000000000000000000000000000000000000000", + "0x72657075746174696f6e00000000000000000000000000000000000000000000") + .setOpTopic0_1(LogOp.AND) + .setOpTopic0_2(LogOp.OR) + .setOpTopic0_3(LogOp.AND) + .setOpTopic1_2(LogOp.OR) + .setOpTopic1_3(LogOp.OR) + .setOpTopic2_3(LogOp.OR) + .build(); + + assertNotNull(quadro); + assertNotNull(quadro.params()); + } + + @Test + void quadroIncorrectTopic2() { + assertThrows(EtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + null, + "0x72657075746174696f6e00000000000000000000000000000000000000000000", + "0x72657075746174696f6e00000000000000000000000000000000000000000000") + .setOpTopic0_1(LogOp.AND) + .setOpTopic0_2(LogOp.OR) + .setOpTopic0_3(LogOp.AND) + .setOpTopic1_2(LogOp.OR) + .setOpTopic1_3(LogOp.OR) + .setOpTopic2_3(LogOp.OR) + .build()); + } + + @Test + void tupleIncorrectTopic2() { + assertThrows(EtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + null) + .setOpTopic0_1(LogOp.AND) + .build()); + } + + @Test + void tupleIncorrectTopic1() { + assertThrows(EtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic(null, + "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") + .setOpTopic0_1(LogOp.AND) + .build()); + } + + @Test + void quadroIncorrectOp1() { + final LogTopicQuadro topicQuadro = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0x72657075746174696f6e00000000000000000000000000000000000000000000", + "0x72657075746174696f6e00000000000000000000000000000000000000000000"); + + assertThrows(EtherScanLogQueryException.class, () -> topicQuadro + .setOpTopic0_1(null) + .setOpTopic0_2(LogOp.OR) + .setOpTopic0_3(LogOp.AND) + .setOpTopic1_2(LogOp.OR) + .setOpTopic1_3(LogOp.OR) + .setOpTopic2_3(LogOp.OR) + .build()); + } + + @Test + void quadroIncorrectOp2() { + final LogTopicQuadro topicQuadro = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0x72657075746174696f6e00000000000000000000000000000000000000000000", + "0x72657075746174696f6e00000000000000000000000000000000000000000000"); + + assertThrows(EtherScanLogQueryException.class, () -> topicQuadro.setOpTopic0_1(LogOp.AND) + .setOpTopic0_2(null) + .setOpTopic0_3(LogOp.AND) + .setOpTopic1_2(LogOp.OR) + .setOpTopic1_3(LogOp.OR) + .setOpTopic2_3(LogOp.OR) + .build()); + } + + @Test + void quadroIncorrectOp3() { + final LogTopicQuadro topicQuadro = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0x72657075746174696f6e00000000000000000000000000000000000000000000", + "0x72657075746174696f6e00000000000000000000000000000000000000000000"); + + assertThrows(EtherScanLogQueryException.class, () -> topicQuadro + .setOpTopic0_1(LogOp.AND) + .setOpTopic0_2(LogOp.OR) + .setOpTopic0_3(null) + .setOpTopic1_2(LogOp.OR) + .setOpTopic1_3(LogOp.OR) + .setOpTopic2_3(LogOp.OR) + .build()); + } + + @Test + void quadroInCorrectAgainTopic() { + assertThrows(EtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0x72657075746174696f6e00000000000000000000000000000000000000000000", + "0x72657075746174696f6e00000000000000000000000000000000000000000000", + null) + .setOpTopic0_1(LogOp.AND) + .setOpTopic0_2(LogOp.OR) + .setOpTopic0_3(LogOp.AND) + .setOpTopic1_2(LogOp.OR) + .setOpTopic1_3(LogOp.OR) + .setOpTopic2_3(LogOp.OR) + .build()); + } + + @Test + void quadroInCorrectOp4() { + final LogTopicQuadro topicQuadro = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0x72657075746174696f6e00000000000000000000000000000000000000000000", + "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545"); + + assertThrows(EtherScanLogQueryException.class, () -> topicQuadro + .setOpTopic0_1(LogOp.AND) + .setOpTopic0_2(LogOp.OR) + .setOpTopic0_3(LogOp.AND) + .setOpTopic1_2(null) + .setOpTopic1_3(LogOp.OR) + .setOpTopic2_3(LogOp.OR) + .build()); + } + + @Test + void quadroInCorrectOp5() { + final LogTopicQuadro topicQuadro = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0x72657075746174696f6e00000000000000000000000000000000000000000000", + "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545"); + + assertThrows(EtherScanLogQueryException.class, () -> topicQuadro + .setOpTopic0_1(LogOp.AND) + .setOpTopic0_2(LogOp.OR) + .setOpTopic0_3(LogOp.AND) + .setOpTopic1_2(LogOp.AND) + .setOpTopic1_3(null) + .setOpTopic2_3(LogOp.OR) + .build()); + } + + @Test + void quadroInCorrectOp6() { + LogTopicQuadro topicQuadro = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0x72657075746174696f6e00000000000000000000000000000000000000000000", + "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545"); + + assertThrows(EtherScanLogQueryException.class, () -> topicQuadro + .setOpTopic0_1(LogOp.AND) + .setOpTopic0_2(LogOp.OR) + .setOpTopic0_3(LogOp.AND) + .setOpTopic1_2(LogOp.AND) + .setOpTopic1_3(LogOp.OR) + .setOpTopic2_3(null) + .build()); + } + + @Test + void quadroInCorrectTopic() { + assertThrows(EtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0x72657075746174696f6e00000000000000000000000000000000000000000000", + "", + "") + .setOpTopic0_1(LogOp.AND) + .setOpTopic0_2(LogOp.OR) + .setOpTopic0_3(LogOp.AND) + .setOpTopic1_2(LogOp.OR) + .setOpTopic1_3(LogOp.OR) + .setOpTopic2_3(LogOp.OR) + .build()); + } +} diff --git a/src/test/java/io/goodforgod/api/etherscan/logs/LogsApiTests.java b/src/test/java/io/goodforgod/api/etherscan/logs/LogsApiTests.java new file mode 100644 index 0000000..0197c5f --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/logs/LogsApiTests.java @@ -0,0 +1,75 @@ +package io.goodforgod.api.etherscan.logs; + +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.model.Log; +import io.goodforgod.api.etherscan.model.query.LogOp; +import io.goodforgod.api.etherscan.model.query.LogQuery; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +/** + * @author GoodforGod + * @since 03.11.2018 + */ +class LogsApiTests extends ApiRunner { + + static Stream source() { + LogQuery single = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") + .build(); + + LogQuery singleInvalidAddr = LogQuery.builder("0x13990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") + .build(); + + LogQuery tupleAnd = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c").withBlockFrom(379224) + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0x72657075746174696f6e00000000000000000000000000000000000000000000") + .setOpTopic0_1(LogOp.AND) + .build(); + + LogQuery tupleOr = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0x72657075746174696f6e00000000000000000000000000000000000000000000") + .setOpTopic0_1(LogOp.OR) + .build(); + + return Stream.of( + Arguments.of(single, 424), + Arguments.of(singleInvalidAddr, 0), + Arguments.of(tupleAnd, 1), + Arguments.of(tupleOr, 426)); + } + + @ParameterizedTest + @MethodSource("source") + void validateQuery(LogQuery query, int logsSize) { + List logs = getApi().logs().logs(query); + assertEquals(logsSize, logs.size()); + + if (logsSize > 0) { + if (logsSize > 1) { + assertNotEquals(logs.get(0), logs.get(1)); + assertNotEquals(logs.get(0).hashCode(), logs.get(1).hashCode()); + } + + assertNotNull(logs.get(0).getAddress()); + assertNotNull(logs.get(0).getBlockNumber()); + assertNotNull(logs.get(0).getData()); + assertNotNull(logs.get(0).getTimeStamp()); + assertNotNull(logs.get(0).getTransactionHash()); + assertNotNull(logs.get(0).getTransactionIndex()); + assertNotNull(logs.get(0).getGasUsed()); + assertNotNull(logs.get(0).getTopics()); + assertNotNull(logs.get(0).getLogIndex()); + assertNotNull(logs.get(0).getGasPrice()); + assertNotNull(logs.get(0).toString()); + + assertEquals(logs.get(0), logs.get(0)); + assertEquals(logs.get(0).hashCode(), logs.get(0).hashCode()); + } + } +} diff --git a/src/test/java/io/goodforgod/api/etherscan/manager/SemaphoreRequestQueueManagerTests.java b/src/test/java/io/goodforgod/api/etherscan/manager/SemaphoreRequestQueueManagerTests.java new file mode 100644 index 0000000..183c442 --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/manager/SemaphoreRequestQueueManagerTests.java @@ -0,0 +1,45 @@ +package io.goodforgod.api.etherscan.manager; + +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.manager.impl.FakeRequestQueueManager; +import io.goodforgod.api.etherscan.manager.impl.SemaphoreRequestQueueManager; +import java.time.Duration; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; + +/** + * @author GoodforGod + * @since 03.11.2018 + */ +class SemaphoreRequestQueueManagerTests extends ApiRunner { + + @Test + void fakeManager() { + RequestQueueManager fakeManager = new FakeRequestQueueManager(); + fakeManager.takeTurn(); + fakeManager.takeTurn(); + fakeManager.takeTurn(); + fakeManager.takeTurn(); + fakeManager.takeTurn(); + fakeManager.takeTurn(); + assertNotNull(fakeManager); + } + + @Test + @Timeout(3500) + void queueManager() { + RequestQueueManager requestQueueManager = new SemaphoreRequestQueueManager(1, Duration.ofSeconds(3)); + requestQueueManager.takeTurn(); + requestQueueManager.takeTurn(); + assertNotNull(requestQueueManager); + } + + @Test + @Timeout(4500) + void queueManagerWithDelay() { + RequestQueueManager requestQueueManager = new SemaphoreRequestQueueManager(1, Duration.ofSeconds(2)); + requestQueueManager.takeTurn(); + requestQueueManager.takeTurn(); + assertNotNull(requestQueueManager); + } +} diff --git a/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java b/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java new file mode 100644 index 0000000..8f9a728 --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java @@ -0,0 +1,510 @@ +package io.goodforgod.api.etherscan.model; + +import io.goodforgod.api.etherscan.model.proxy.BlockProxy; +import io.goodforgod.api.etherscan.model.proxy.ReceiptProxy; +import io.goodforgod.api.etherscan.model.proxy.TxProxy; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.LocalDateTime; +import java.util.Arrays; +import java.util.Collections; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +/** + * @author Anton Kurako (GoodforGod) + * @since 14.05.2023 + */ +class ModelBuilderTests extends Assertions { + + @Test + void abiBuilder() { + Abi value = Abi.builder() + .withContractAbi("1") + .withIsVerified(true) + .build(); + + assertNotNull(value); + assertTrue(value.isVerified()); + assertEquals("1", value.getContractAbi()); + } + + @Test + void blockBuilder() { + LocalDateTime timestamp = LocalDateTime.now(); + Block value = Block.builder() + .withBlockNumber(1) + .withBlockReward(BigInteger.ONE) + .withTimeStamp(timestamp) + .build(); + + assertNotNull(value); + assertEquals(1, value.getBlockNumber()); + assertEquals(BigInteger.ONE, value.getBlockReward()); + assertEquals(timestamp, value.getTimeStamp()); + } + + @Test + void blockUncleBuilder() { + LocalDateTime timestamp = LocalDateTime.now(); + BlockUncle value = BlockUncle.builder() + .withBlockNumber(1) + .withBlockReward(BigInteger.ONE) + .withTimeStamp(timestamp) + .withBlockMiner("1") + .withUncleInclusionReward("1") + .withUncles(Collections.singletonList(BlockUncle.Uncle.builder() + .withBlockreward(BigInteger.ONE) + .withMiner("1") + .withUnclePosition(1) + .build())) + .build(); + + assertNotNull(value); + assertEquals(1, value.getBlockNumber()); + assertEquals(BigInteger.ONE, value.getBlockReward()); + assertEquals(timestamp, value.getTimeStamp()); + } + + @Test + void gasOracleBuilder() { + GasOracle value = GasOracle.builder() + .withFastGasPrice(Wei.ofWei(1000000000)) + .withProposeGasPrice(Wei.ofWei(1000000000)) + .withSafeGasPrice(Wei.ofWei(1000000000)) + .withGasUsedRatio(Collections.singletonList(new BigDecimal(1))) + .withLastBlock(1L) + .withSuggestBaseFee(BigDecimal.valueOf(1.0)) + .build(); + + assertNotNull(value); + assertEquals(Wei.ofWei(1000000000), value.getFastGasPriceInWei()); + + GasOracle value2 = GasOracle.builder() + .withFastGasPrice(Wei.ofWei(1000000000)) + .withProposeGasPrice(Wei.ofWei(1000000000)) + .withSafeGasPrice(Wei.ofWei(1000000000)) + .withGasUsedRatio(Collections.singletonList(new BigDecimal(1))) + .withLastBlock(1L) + .withSuggestBaseFee(BigDecimal.valueOf(1.0)) + .build(); + assertEquals(value, value2); + assertEquals(value.hashCode(), value2.hashCode()); + assertEquals(value.toString(), value2.toString()); + } + + @Test + void logBuilder() { + LocalDateTime timestamp = LocalDateTime.now(); + Log value = Log.builder() + .withAddress("1") + .withBlockNumber(1L) + .withData("1") + .withGasPrice(Wei.ofWei(1)) + .withGasUsed(Wei.ofWei(1)) + .withLogIndex(1L) + .withTimeStamp(timestamp) + .withTransactionHash("1") + .withTransactionIndex(1L) + .withTopics(Collections.singletonList("1")) + .build(); + + assertNotNull(value); + assertEquals(1, value.getTopics().size()); + } + + @Test + void priceBuilder() { + LocalDateTime timestamp = LocalDateTime.now(); + Price value = Price.builder() + .withBtc(BigDecimal.valueOf(1.0)) + .withUsd(BigDecimal.valueOf(1.0)) + .withTimestampBtc(timestamp) + .withTimestampUsd(timestamp) + .build(); + + assertNotNull(value); + assertEquals(BigDecimal.valueOf(1.0), value.inUsd()); + assertEquals(BigDecimal.valueOf(1.0), value.inBtc()); + } + + @Test + void statusBuilder() { + Status value = Status.builder() + .withIsError(1) + .withErrDescription("1") + .build(); + + assertNotNull(value); + assertTrue(value.haveError()); + assertEquals("1", value.getErrDescription()); + } + + @Test + void txBuilder() { + LocalDateTime timestamp = LocalDateTime.now(); + Tx value = Tx.builder() + .withBlockHash("1") + .withBlockNumber(1L) + .withConfirmations(1L) + .withContractAddress("1") + .withFrom("1") + .withTo("1") + .withCumulativeGasUsed(Wei.ofWei(BigInteger.ONE)) + .withGas(Wei.ofWei(BigInteger.ONE)) + .withGasPrice(Wei.ofWei(BigInteger.ONE)) + .withGasUsed(Wei.ofWei(BigInteger.ONE)) + .withHash("1") + .withInput("1") + .withIsError("1") + .withNonce(1L) + .withTimeStamp(timestamp) + .withValue(BigInteger.ONE) + .withTransactionIndex(1) + .withTxReceiptStatus("1") + .build(); + + assertNotNull(value); + assertTrue(value.haveError()); + assertEquals("1", value.getTo()); + assertEquals("1", value.getFrom()); + + Tx value2 = Tx.builder() + .withBlockHash("1") + .withBlockNumber(1L) + .withConfirmations(1L) + .withContractAddress("1") + .withFrom("1") + .withTo("1") + .withCumulativeGasUsed(Wei.ofWei(BigInteger.ONE)) + .withGas(Wei.ofWei(BigInteger.ONE)) + .withGasPrice(Wei.ofWei(BigInteger.ONE)) + .withGasUsed(Wei.ofWei(BigInteger.ONE)) + .withHash("1") + .withInput("1") + .withIsError("1") + .withNonce(1L) + .withTimeStamp(timestamp) + .withValue(BigInteger.ONE) + .withTransactionIndex(1) + .withTxReceiptStatus("1") + .build(); + + assertEquals(value, value2); + assertEquals(value.hashCode(), value2.hashCode()); + assertEquals(value.toString(), value2.toString()); + assertEquals(0, value.compareTo(value2)); + } + + @Test + void txErc20Builder() { + LocalDateTime timestamp = LocalDateTime.now(); + TxErc20 value = TxErc20.builder() + .withBlockHash("1") + .withBlockNumber(1L) + .withConfirmations(1L) + .withContractAddress("1") + .withFrom("1") + .withTo("1") + .withCumulativeGasUsed(Wei.ofWei(BigInteger.ONE)) + .withGas(Wei.ofWei(BigInteger.ONE)) + .withGasPrice(Wei.ofWei(BigInteger.ONE)) + .withGasUsed(Wei.ofWei(BigInteger.ONE)) + .withHash("1") + .withInput("1") + .withTokenName("1") + .withTokenSymbol("1") + .withTokenDecimal("1") + .withNonce(1L) + .withTimeStamp(timestamp) + .withValue(BigInteger.ONE) + .withTransactionIndex(1) + .build(); + + assertNotNull(value); + assertEquals("1", value.getTo()); + assertEquals("1", value.getFrom()); + } + + @Test + void txErc721Builder() { + LocalDateTime timestamp = LocalDateTime.now(); + TxErc721 value = TxErc721.builder() + .withBlockHash("1") + .withBlockNumber(1L) + .withConfirmations(1L) + .withContractAddress("1") + .withFrom("1") + .withTo("1") + .withCumulativeGasUsed(Wei.ofWei(BigInteger.ONE)) + .withGas(Wei.ofWei(BigInteger.ONE)) + .withGasPrice(Wei.ofWei(BigInteger.ONE)) + .withGasUsed(Wei.ofWei(BigInteger.ONE)) + .withHash("1") + .withInput("1") + .withTokenName("1") + .withTokenSymbol("1") + .withTokenDecimal("1") + .withTokenID("1") + .withNonce(1L) + .withTimeStamp(timestamp) + .withTransactionIndex(1) + .build(); + + assertNotNull(value); + assertEquals("1", value.getTo()); + assertEquals("1", value.getFrom()); + } + + @Test + void txErc1155Builder() { + LocalDateTime timestamp = LocalDateTime.now(); + TxErc1155 value = TxErc1155.builder() + .withBlockHash("1") + .withBlockNumber(1L) + .withConfirmations(1L) + .withContractAddress("1") + .withFrom("1") + .withTo("1") + .withCumulativeGasUsed(Wei.ofWei(BigInteger.ONE)) + .withGas(Wei.ofWei(BigInteger.ONE)) + .withGasPrice(Wei.ofWei(BigInteger.ONE)) + .withGasUsed(Wei.ofWei(BigInteger.ONE)) + .withHash("1") + .withInput("1") + .withTokenName("1") + .withTokenSymbol("1") + .withTokenDecimal("1") + .withTokenID("1") + .withNonce(1L) + .withTimeStamp(timestamp) + .withTransactionIndex(1) + .build(); + + assertNotNull(value); + assertEquals("1", value.getTo()); + assertEquals("1", value.getFrom()); + + TxErc1155 value2 = TxErc1155.builder() + .withBlockHash("1") + .withBlockNumber(1L) + .withConfirmations(1L) + .withContractAddress("1") + .withFrom("1") + .withTo("1") + .withCumulativeGasUsed(Wei.ofWei(BigInteger.ONE)) + .withGas(Wei.ofWei(BigInteger.ONE)) + .withGasPrice(Wei.ofWei(BigInteger.ONE)) + .withGasUsed(Wei.ofWei(BigInteger.ONE)) + .withHash("1") + .withInput("1") + .withTokenName("1") + .withTokenSymbol("1") + .withTokenDecimal("1") + .withTokenID("1") + .withNonce(1L) + .withTimeStamp(timestamp) + .withTransactionIndex(1) + .build(); + + assertEquals(value, value2); + assertEquals(value.hashCode(), value2.hashCode()); + assertEquals(value.toString(), value2.toString()); + } + + @Test + void txInternalBuilder() { + LocalDateTime timestamp = LocalDateTime.now(); + TxInternal value = TxInternal.builder() + .withBlockNumber(1L) + .withContractAddress("1") + .withFrom("1") + .withTo("1") + .withValue(BigInteger.ONE) + .withGas(Wei.ofWei(BigInteger.ONE)) + .withGasUsed(Wei.ofWei(BigInteger.ONE)) + .withHash("1") + .withInput("1") + .withTimeStamp(timestamp) + .withErrCode("1") + .withIsError(1) + .withTraceId("1") + .withType("1") + .build(); + + assertNotNull(value); + assertEquals("1", value.getTo()); + assertEquals("1", value.getFrom()); + + TxInternal value2 = TxInternal.builder() + .withBlockNumber(1L) + .withContractAddress("1") + .withFrom("1") + .withTo("1") + .withValue(BigInteger.ONE) + .withGas(Wei.ofWei(BigInteger.ONE)) + .withGasUsed(Wei.ofWei(BigInteger.ONE)) + .withHash("1") + .withInput("1") + .withTimeStamp(timestamp) + .withErrCode("1") + .withIsError(1) + .withTraceId("1") + .withType("1") + .build(); + + assertEquals(value, value2); + assertEquals(value.hashCode(), value2.hashCode()); + assertEquals(value.toString(), value2.toString()); + } + + @Test + void ethSupplyBuilder() { + EthSupply value = EthSupply.builder() + .withBurntFees(Wei.ofWei(1)) + .withEth2Staking(Wei.ofWei(1)) + .withEthSupply(Wei.ofWei(1)) + .withWithdrawnTotal(Wei.ofWei(1)) + .build(); + + assertNotNull(value); + assertEquals(BigInteger.valueOf(1), value.getTotal().asWei()); + + EthSupply valueEmpty = EthSupply.builder() + .build(); + assertNotNull(valueEmpty); + assertEquals(BigInteger.ZERO, valueEmpty.getTotal().asWei()); + + EthSupply value2 = EthSupply.builder() + .withBurntFees(Wei.ofWei(1)) + .withEth2Staking(Wei.ofWei(1)) + .withEthSupply(Wei.ofWei(1)) + .withWithdrawnTotal(Wei.ofWei(1)) + .build(); + assertEquals(value, value2); + assertEquals(value.hashCode(), value2.hashCode()); + assertEquals(value.toString(), value2.toString()); + } + + @Test + void receiptProxyBuilder() { + LocalDateTime timestamp = LocalDateTime.now(); + ReceiptProxy value = ReceiptProxy.builder() + .withBlockHash("1") + .withBlockNumber(1L) + .withContractAddress("1") + .withCumulativeGasUsed(Wei.ofWei(1)) + .withFrom("1") + .withTo("1") + .withGasUsed(Wei.ofWei(1)) + .withRoot("1") + .withLogsBloom("1") + .withTransactionHash("1") + .withTransactionIndex(1L) + .withLogs(Arrays.asList(Log.builder() + .withTopics(Arrays.asList("1")) + .withTransactionIndex(1L) + .withTransactionHash("1") + .withTimeStamp(timestamp) + .withLogIndex(1L) + .withGasUsed(Wei.ofWei(1)) + .withGasPrice(Wei.ofWei(1)) + .withData("1") + .withAddress("1") + .build())) + .build(); + + assertNotNull(value); + assertEquals(BigInteger.valueOf(1), value.getGasUsed().asWei()); + } + + @Test + void blockProxyBuilder() { + LocalDateTime timestamp = LocalDateTime.now(); + BlockProxy value = BlockProxy.builder() + .withGasUsed(Wei.ofWei(1)) + .withLogsBloom("1") + .withDifficulty("1") + .withExtraData("1") + .withGasLimit(Wei.ofWei(1)) + .withHash("1") + .withMiner("1") + .withMixHash("1") + .withNonce("1") + .withNumber(1L) + .withParentHash("1") + .withReceiptsRoot("1") + .withSha3Uncles("1") + .withSize(1L) + .withStateRoot("1") + .withTimestamp(timestamp) + .withTotalDifficulty("1") + .withTransactionsRoot("1") + .withUncles(Arrays.asList("1")) + .withTransactions(Arrays.asList(TxProxy.builder() + .withBlockHash("1") + .withBlockNumber(1L) + .withFrom("1") + .withGas(Wei.ofWei(1)) + .withGasPrice(Wei.ofWei(1)) + .withHash("1") + .withInput("1") + .withNonce(1L) + .withR("1") + .withS("1") + .withTo("1") + .withTransactionIndex(1L) + .withV("1") + .withValue("1") + .withV("1") + .build())) + .build(); + + assertNotNull(value); + assertEquals(BigInteger.valueOf(1), value.getGasUsed().asWei()); + } + + @Test + void weiTests() { + Wei w1 = Wei.ofWei(1); + Wei w2 = Wei.ofWei(1L); + Wei w3 = Wei.ofWei(BigInteger.valueOf(1)); + assertEquals(w1, w2); + assertEquals(w1, w3); + assertEquals(w1.hashCode(), w2.hashCode()); + assertEquals(w1.hashCode(), w3.hashCode()); + assertEquals(w1.toString(), w3.toString()); + + Wei kw1 = Wei.ofKwei(1); + Wei kw2 = Wei.ofKwei(1L); + Wei kw3 = Wei.ofKwei(BigInteger.valueOf(1)); + Wei kw4 = Wei.ofKwei(BigDecimal.valueOf(1)); + assertEquals(kw1, kw2); + assertEquals(kw1, kw3); + assertEquals(kw1, kw4); + + Wei mw1 = Wei.ofMwei(1); + Wei mw2 = Wei.ofMwei(1L); + Wei mw3 = Wei.ofMwei(BigInteger.valueOf(1)); + Wei mw4 = Wei.ofMwei(BigDecimal.valueOf(1)); + assertEquals(mw1, mw2); + assertEquals(mw1, mw3); + assertEquals(mw1, mw4); + + Wei gw1 = Wei.ofGwei(1); + Wei gw2 = Wei.ofGwei(1L); + Wei gw3 = Wei.ofGwei(BigInteger.valueOf(1)); + Wei gw4 = Wei.ofGwei(BigDecimal.valueOf(1)); + assertEquals(gw1, gw2); + assertEquals(gw1, gw3); + assertEquals(gw1, gw4); + + Wei ew1 = Wei.ofEther(1); + Wei ew2 = Wei.ofEther(1L); + Wei ew3 = Wei.ofEther(BigInteger.valueOf(1)); + Wei ew4 = Wei.ofEther(BigDecimal.valueOf(1)); + assertEquals(ew1, ew2); + assertEquals(ew1, ew3); + assertEquals(ew1, ew4); + } +} diff --git a/src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTests.java similarity index 59% rename from src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTests.java index 5d3884d..363d5a2 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTests.java @@ -1,32 +1,19 @@ -package io.api.etherscan.proxy; - -import io.api.ApiRunner; -import io.api.etherscan.core.impl.EtherScanApi; -import io.api.etherscan.manager.impl.QueueManager; -import io.api.etherscan.model.EthNetwork; -import io.api.etherscan.model.proxy.BlockProxy; -import org.junit.Test; +package io.goodforgod.api.etherscan.proxy; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.model.proxy.BlockProxy; import java.util.Optional; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class ProxyBlockApiTest extends ApiRunner { - - private final EtherScanApi api; - - public ProxyBlockApiTest() { - final QueueManager queueManager = new QueueManager(1, 5100L, 5100L, 0); - this.api = new EtherScanApi(getApiKey(), EthNetwork.MAINNET, queueManager); - } +class ProxyBlockApiTests extends ApiRunner { @Test - public void correct() { - Optional block = api.proxy().block(5120); + void correct() { + Optional block = getApi().proxy().block(5120); assertTrue(block.isPresent()); BlockProxy proxy = block.get(); assertNotNull(proxy.getHash()); @@ -52,20 +39,20 @@ public void correct() { assertNotNull(proxy.getUncles()); assertNotNull(proxy.toString()); - BlockProxy empty = new BlockProxy(); + BlockProxy empty = BlockProxy.builder().build(); assertNotEquals(proxy, empty); assertNotEquals(proxy.hashCode(), empty.hashCode()); } @Test - public void correctParamWithEmptyExpectedResult() { - Optional block = api.proxy().block(99999999999L); + void correctParamWithEmptyExpectedResult() { + Optional block = getApi().proxy().block(99999999999L); assertFalse(block.isPresent()); } @Test - public void correctParamNegativeNo() { - Optional block = api.proxy().block(-1); + void correctParamNegativeNo() { + Optional block = getApi().proxy().block(-1); assertTrue(block.isPresent()); assertNotNull(block.get().getHash()); } diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockLastNoApiTests.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockLastNoApiTests.java new file mode 100644 index 0000000..568d9ae --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockLastNoApiTests.java @@ -0,0 +1,17 @@ +package io.goodforgod.api.etherscan.proxy; + +import io.goodforgod.api.etherscan.ApiRunner; +import org.junit.jupiter.api.Test; + +/** + * @author GoodforGod + * @since 13.11.2018 + */ +class ProxyBlockLastNoApiTests extends ApiRunner { + + @Test + void correct() { + long noLast = getApi().proxy().blockNoLast(); + assertNotEquals(0, noLast); + } +} diff --git a/src/test/java/io/api/etherscan/proxy/ProxyBlockUncleApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockUncleApiTests.java similarity index 62% rename from src/test/java/io/api/etherscan/proxy/ProxyBlockUncleApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockUncleApiTests.java index 474c5bb..01725c5 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyBlockUncleApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockUncleApiTests.java @@ -1,21 +1,18 @@ -package io.api.etherscan.proxy; - -import io.api.ApiRunner; -import io.api.etherscan.model.proxy.BlockProxy; -import org.junit.Test; +package io.goodforgod.api.etherscan.proxy; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.model.proxy.BlockProxy; import java.util.Optional; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 13.11.2018 */ -public class ProxyBlockUncleApiTest extends ApiRunner { +class ProxyBlockUncleApiTests extends ApiRunner { @Test - public void correct() { + void correct() { Optional block = getApi().proxy().blockUncle(603183, 0); assertTrue(block.isPresent()); assertNotNull(block.get().getHash()); @@ -23,13 +20,13 @@ public void correct() { } @Test - public void correctParamWithEmptyExpectedResult() { + void correctParamWithEmptyExpectedResult() { Optional block = getApi().proxy().blockUncle(5120, 1); assertFalse(block.isPresent()); } @Test - public void correctParamNegativeNo() { + void correctParamNegativeNo() { Optional block = getApi().proxy().blockUncle(-603183, 0); assertFalse(block.isPresent()); } diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCallApiTests.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCallApiTests.java new file mode 100644 index 0000000..d5168c6 --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCallApiTests.java @@ -0,0 +1,45 @@ +package io.goodforgod.api.etherscan.proxy; + +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.error.EtherScanInvalidDataHexException; +import io.goodforgod.api.etherscan.util.BasicUtils; +import java.util.Optional; +import org.junit.jupiter.api.Test; + +/** + * @author GoodforGod + * @since 03.11.2018 + */ +class ProxyCallApiTests extends ApiRunner { + + @Test + void correct() { + Optional call = getApi().proxy().call("0xAEEF46DB4855E25702F8237E8f403FddcaF931C0", + "0x70a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724"); + assertTrue(call.isPresent()); + assertFalse(BasicUtils.isNotHex(call.get())); + } + + @Test + void invalidParamWithError() { + assertThrows(EtherScanInvalidAddressException.class, + () -> getApi().proxy().call("0xEEF46DB4855E25702F8237E8f403FddcaF931C0", + "0x70a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724")); + } + + @Test + void invalidParamNotHex() { + assertThrows(EtherScanInvalidDataHexException.class, + () -> getApi().proxy().call("0xAEEF46DB4855E25702F8237E8f403FddcaF931C0", + "7-0a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724")); + } + + @Test + void correctParamWithEmptyExpectedResult() { + Optional call = getApi().proxy().call("0xAEEF16DB4855E25702F8237E8f403FddcaF931C0", + "0x70a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724"); + assertTrue(call.isPresent()); + assertFalse(BasicUtils.isNotHex(call.get()), call.get()); + } +} diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCodeApiTests.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCodeApiTests.java new file mode 100644 index 0000000..1e3c696 --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCodeApiTests.java @@ -0,0 +1,34 @@ +package io.goodforgod.api.etherscan.proxy; + +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.util.BasicUtils; +import java.util.Optional; +import org.junit.jupiter.api.Test; + +/** + * @author GoodforGod + * @since 03.11.2018 + */ +class ProxyCodeApiTests extends ApiRunner { + + @Test + void correct() { + Optional call = getApi().proxy().code("0xf75e354c5edc8efed9b59ee9f67a80845ade7d0c"); + assertTrue(call.isPresent()); + assertFalse(BasicUtils.isNotHex(call.get()), call.get()); + } + + @Test + void invalidParamWithError() { + assertThrows(EtherScanInvalidAddressException.class, + () -> getApi().proxy().code("0f75e354c5edc8efed9b59ee9f67a80845ade7d0c")); + } + + @Test + void correctParamWithEmptyExpectedResult() { + Optional call = getApi().proxy().code("0xf15e354c5edc8efed9b59ee9f67a80845ade7d0c"); + assertTrue(call.isPresent()); + assertFalse(BasicUtils.isNotHex(call.get()), call.get()); + } +} diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyGasApiTests.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyGasApiTests.java new file mode 100644 index 0000000..4dea82e --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyGasApiTests.java @@ -0,0 +1,43 @@ +package io.goodforgod.api.etherscan.proxy; + +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidDataHexException; +import io.goodforgod.api.etherscan.model.Wei; +import org.junit.jupiter.api.Test; + +/** + * @author GoodforGod + * @since 03.11.2018 + */ +class ProxyGasApiTests extends ApiRunner { + + @Test + void correctPrice() { + Wei price = getApi().proxy().gasPrice(); + assertNotNull(price); + assertNotEquals(0, price.asWei().intValue()); + } + + @Test + void correctEstimated() { + Wei price = getApi().proxy().gasEstimated(); + assertNotNull(price); + assertNotEquals(0, price.asWei().intValue()); + } + + @Test + void correctEstimatedWithData() { + String dataCustom = "606060405260728060106000396000f360606040526000606060405260728060106000396000f360606040526000"; + Wei price = getApi().proxy().gasEstimated(); + Wei priceCustom = getApi().proxy().gasEstimated(dataCustom); + assertNotNull(price); + assertNotNull(priceCustom); + assertNotEquals(price, priceCustom); + } + + @Test + void invalidParamWithError() { + String dataCustom = "280&60106000396000f360606040526000"; + assertThrows(EtherScanInvalidDataHexException.class, () -> getApi().proxy().gasEstimated(dataCustom)); + } +} diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyStorageApiTests.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyStorageApiTests.java new file mode 100644 index 0000000..3c6d221 --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyStorageApiTests.java @@ -0,0 +1,31 @@ +package io.goodforgod.api.etherscan.proxy; + +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import java.util.Optional; +import org.junit.jupiter.api.Test; + +/** + * @author GoodforGod + * @since 03.11.2018 + */ +class ProxyStorageApiTests extends ApiRunner { + + @Test + void correct() { + Optional call = getApi().proxy().storageAt("0x6e03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 0); + assertFalse(call.isPresent()); + } + + @Test + void invalidParamWithError() { + assertThrows(EtherScanInvalidAddressException.class, + () -> getApi().proxy().storageAt("0xe03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 0)); + } + + @Test + void correctParamWithEmptyExpectedResult() { + final Optional call = getApi().proxy().storageAt("0x6e03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 10000); + assertFalse(call.isPresent()); + } +} diff --git a/src/test/java/io/api/etherscan/proxy/ProxyTxApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxApiTests.java similarity index 70% rename from src/test/java/io/api/etherscan/proxy/ProxyTxApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxApiTests.java index 2779120..b20369e 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyTxApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxApiTests.java @@ -1,22 +1,19 @@ -package io.api.etherscan.proxy; - -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidTxHashException; -import io.api.etherscan.model.proxy.TxProxy; -import org.junit.Test; +package io.goodforgod.api.etherscan.proxy; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidTxHashException; +import io.goodforgod.api.etherscan.model.proxy.TxProxy; import java.util.Optional; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class ProxyTxApiTest extends ApiRunner { +class ProxyTxApiTests extends ApiRunner { @Test - public void correctByHash() { + void correctByHash() { Optional tx = getApi().proxy().tx("0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); assertTrue(tx.isPresent()); assertNotNull(tx.get().getBlockHash()); @@ -27,13 +24,13 @@ public void correctByHash() { assertNotNull(tx.get().getBlockNumber()); assertNotNull(tx.get().toString()); - TxProxy empty = new TxProxy(); + TxProxy empty = TxProxy.builder().build(); assertNotEquals(tx.get(), empty); assertNotEquals(tx.get().hashCode(), empty.hashCode()); } @Test - public void correctByBlockNo() { + void correctByBlockNo() { Optional tx = getApi().proxy().tx(637368, 0); assertTrue(tx.isPresent()); assertNotNull(tx.get().getBlockHash()); @@ -52,19 +49,20 @@ public void correctByBlockNo() { assertNotNull(tx.get().getInput()); } - @Test(expected = InvalidTxHashException.class) - public void invalidParamWithError() { - Optional tx = getApi().proxy().tx("0xe2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); + @Test + void invalidParamWithError() { + assertThrows(EtherScanInvalidTxHashException.class, + () -> getApi().proxy().tx("0xe2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1")); } @Test - public void correctParamWithEmptyExpectedResultBlockNoExist() { + void correctParamWithEmptyExpectedResultBlockNoExist() { Optional tx = getApi().proxy().tx(99999999L, 0); assertFalse(tx.isPresent()); } @Test - public void correctParamWithEmptyExpectedResult() { + void correctParamWithEmptyExpectedResult() { Optional tx = getApi().proxy().tx("0x2e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); assertFalse(tx.isPresent()); } diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxCountApiTests.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxCountApiTests.java new file mode 100644 index 0000000..95ed859 --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxCountApiTests.java @@ -0,0 +1,42 @@ +package io.goodforgod.api.etherscan.proxy; + +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import org.junit.jupiter.api.Test; + +/** + * @author GoodforGod + * @since 03.11.2018 + */ +class ProxyTxCountApiTests extends ApiRunner { + + @Test + void correctSended() { + int count = getApi().proxy().txSendCount("0x2910543af39aba0cd09dbb2d50200b3e800a63d2"); + assertNotEquals(0, count); + } + + @Test + void correctByBlockNo() { + int count = getApi().proxy().txCount(6137420); + assertNotEquals(0, count); + } + + @Test + void invalidParamWithError() { + assertThrows(EtherScanInvalidAddressException.class, + () -> getApi().proxy().txSendCount("0xe03d9cce9d60f3e9f2597e13cd4c54c55330cfd")); + } + + @Test + void correctParamWithEmptyExpectedResultBlockNoExist() { + int count = getApi().proxy().txCount(99999999999L); + assertNotEquals(1, count); + } + + @Test + void correctParamWithEmptyExpectedResult() { + int count = getApi().proxy().txSendCount("0x1e03d9cce9d60f3e9f2597e13cd4c54c55330cfd"); + assertNotEquals(1, count); + } +} diff --git a/src/test/java/io/api/etherscan/proxy/ProxyTxReceiptApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxReceiptApiTests.java similarity index 68% rename from src/test/java/io/api/etherscan/proxy/ProxyTxReceiptApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxReceiptApiTests.java index c4a3383..662fec2 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyTxReceiptApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxReceiptApiTests.java @@ -1,22 +1,19 @@ -package io.api.etherscan.proxy; - -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidTxHashException; -import io.api.etherscan.model.proxy.ReceiptProxy; -import org.junit.Test; +package io.goodforgod.api.etherscan.proxy; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidTxHashException; +import io.goodforgod.api.etherscan.model.proxy.ReceiptProxy; import java.util.Optional; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class ProxyTxReceiptApiTest extends ApiRunner { +class ProxyTxReceiptApiTests extends ApiRunner { @Test - public void correct() { + void correct() { Optional infoProxy = getApi().proxy() .txReceipt("0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); assertTrue(infoProxy.isPresent()); @@ -29,25 +26,25 @@ public void correct() { assertNotNull(infoProxy.get().getTransactionHash()); assertNotNull(infoProxy.get().getTransactionIndex()); assertNotNull(infoProxy.get().getGasUsed()); - assertNotNull(infoProxy.get().getCumulativeGasUsed()); + assertNotNull(infoProxy.get().getGasUsedCumulative()); assertNotNull(infoProxy.get().getLogs()); assertNotNull(infoProxy.get().getLogsBloom()); assertNull(infoProxy.get().getContractAddress()); assertNotNull(infoProxy.get().toString()); - ReceiptProxy empty = new ReceiptProxy(); + ReceiptProxy empty = ReceiptProxy.builder().build(); assertNotEquals(empty, infoProxy.get()); assertNotEquals(empty.hashCode(), infoProxy.get().hashCode()); } - @Test(expected = InvalidTxHashException.class) - public void invalidParamWithError() { - Optional infoProxy = getApi().proxy() - .txReceipt("0xe2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); + @Test + void invalidParamWithError() { + assertThrows(EtherScanInvalidTxHashException.class, () -> getApi().proxy() + .txReceipt("0xe2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1")); } @Test - public void correctParamWithEmptyExpectedResult() { + void correctParamWithEmptyExpectedResult() { Optional infoProxy = getApi().proxy() .txReceipt("0x2e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); assertFalse(infoProxy.isPresent()); diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxSendRawApiTests.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxSendRawApiTests.java new file mode 100644 index 0000000..3910bf8 --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxSendRawApiTests.java @@ -0,0 +1,36 @@ +package io.goodforgod.api.etherscan.proxy; + +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidDataHexException; +import io.goodforgod.api.etherscan.error.EtherScanResponseException; +import java.util.Optional; +import org.junit.jupiter.api.Test; + +/** + * @author GoodforGod + * @since 03.11.2018 + */ +// TODO contact etherscan and ask about method behavior +class ProxyTxSendRawApiTests extends ApiRunner { + + void correct() { + Optional sendRaw = getApi().proxy() + .txSendRaw("0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); + assertTrue(sendRaw.isPresent()); + } + + @Test + void invalidParamWithError() { + assertThrows(EtherScanInvalidDataHexException.class, () -> getApi().proxy().txSendRaw("5151=0561")); + } + + @Test + void invalidParamEtherScanDataException() { + assertThrows(EtherScanResponseException.class, () -> getApi().proxy().txSendRaw("0x1")); + } + + void correctParamWithEmptyExpectedResult() { + Optional sendRaw = getApi().proxy().txSendRaw("0x000000"); + assertFalse(sendRaw.isPresent()); + } +} diff --git a/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTests.java b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTests.java new file mode 100644 index 0000000..76b87d5 --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTests.java @@ -0,0 +1,27 @@ +package io.goodforgod.api.etherscan.statistic; + +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.model.Price; +import org.junit.jupiter.api.Test; + +/** + * @author GoodforGod + * @since 03.11.2018 + */ +class StatisticPriceApiTests extends ApiRunner { + + @Test + void correct() { + Price price = getApi().stats().priceLast(); + assertNotNull(price); + assertNotNull(price.timestampBtc()); + assertNotNull(price.timestampUsd()); + assertNotEquals(0.0, price.inBtc().doubleValue()); + assertNotEquals(0.0, price.inUsd().doubleValue()); + assertNotNull(price.toString()); + + Price empty = Price.builder().build(); + assertNotEquals(price, empty); + assertNotEquals(price.hashCode(), empty.hashCode()); + } +} diff --git a/src/test/java/io/api/etherscan/statistic/StatisticSupplyApiTest.java b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyApiTests.java similarity index 53% rename from src/test/java/io/api/etherscan/statistic/StatisticSupplyApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyApiTests.java index a705a31..6564c93 100644 --- a/src/test/java/io/api/etherscan/statistic/StatisticSupplyApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyApiTests.java @@ -1,31 +1,28 @@ -package io.api.etherscan.statistic; - -import io.api.ApiRunner; -import io.api.etherscan.model.Supply; -import org.junit.Test; +package io.goodforgod.api.etherscan.statistic; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.model.Wei; import java.math.BigInteger; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class StatisticSupplyApiTest extends ApiRunner { +class StatisticSupplyApiTests extends ApiRunner { @Test - public void correct() { - Supply supply = getApi().stats().supply(); + void correct() { + Wei supply = getApi().stats().supply(); assertNotNull(supply); - assertNotNull(supply.getValue()); + assertNotNull(supply.asWei()); assertNotNull(supply.asGwei()); assertNotNull(supply.asKwei()); assertNotNull(supply.asMwei()); assertNotNull(supply.asEther()); assertNotNull(supply.toString()); - Supply empty = new Supply(BigInteger.ONE); + Wei empty = Wei.ofWei(BigInteger.ONE); assertNotEquals(supply, empty); assertNotEquals(supply.hashCode(), empty.hashCode()); } diff --git a/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyTotalApiTests.java b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyTotalApiTests.java new file mode 100644 index 0000000..b6098d8 --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyTotalApiTests.java @@ -0,0 +1,28 @@ +package io.goodforgod.api.etherscan.statistic; + +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.model.EthSupply; +import org.junit.jupiter.api.Test; + +/** + * @author GoodforGod + * @since 14.05.2023 + */ +class StatisticSupplyTotalApiTests extends ApiRunner { + + @Test + void correct() { + EthSupply supply = getApi().stats().supplyTotal(); + assertNotNull(supply); + assertNotNull(supply.getBurntFees()); + assertNotEquals(0, supply.getBurntFees().asWei().intValue()); + assertNotNull(supply.getEthSupply()); + assertNotEquals(0, supply.getEthSupply().asWei().intValue()); + assertNotNull(supply.getEth2Staking()); + assertNotEquals(0, supply.getEth2Staking().asWei().intValue()); + assertNotNull(supply.getWithdrawnTotal()); + assertNotEquals(0, supply.getWithdrawnTotal().asWei().intValue()); + assertNotNull(supply.getTotal()); + assertNotEquals(0, supply.getTotal().asWei().intValue()); + } +} diff --git a/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticTokenSupplyApiTests.java b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticTokenSupplyApiTests.java new file mode 100644 index 0000000..6eff846 --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticTokenSupplyApiTests.java @@ -0,0 +1,34 @@ +package io.goodforgod.api.etherscan.statistic; + +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.model.Wei; +import java.math.BigInteger; +import org.junit.jupiter.api.Test; + +/** + * @author GoodforGod + * @since 03.11.2018 + */ +class StatisticTokenSupplyApiTests extends ApiRunner { + + @Test + void correct() { + Wei supply = getApi().stats().supply("0x57d90b64a1a57749b0f932f1a3395792e12e7055"); + assertNotNull(supply); + assertNotEquals(BigInteger.ZERO, supply.asWei()); + } + + @Test + void invalidParamWithError() { + assertThrows(EtherScanInvalidAddressException.class, + () -> getApi().stats().supply("0x7d90b64a1a57749b0f932f1a3395792e12e7055")); + } + + @Test + void correctParamWithEmptyExpectedResult() { + Wei supply = getApi().stats().supply("0x51d90b64a1a57749b0f932f1a3395792e12e7055"); + assertNotNull(supply); + assertEquals(0, supply.asEther().intValue()); + } +} diff --git a/src/test/java/io/api/support/AddressUtil.java b/src/test/java/io/goodforgod/api/etherscan/support/AddressUtil.java similarity index 95% rename from src/test/java/io/api/support/AddressUtil.java rename to src/test/java/io/goodforgod/api/etherscan/support/AddressUtil.java index 7949b9e..fa007db 100644 --- a/src/test/java/io/api/support/AddressUtil.java +++ b/src/test/java/io/goodforgod/api/etherscan/support/AddressUtil.java @@ -1,18 +1,16 @@ -package io.api.support; +package io.goodforgod.api.etherscan.support; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ThreadLocalRandom; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ public class AddressUtil { - public static List genFakeAddresses(int size) { + static List genFakeAddresses(int size) { final List addresses = new ArrayList<>(); for (int i = 0; i < size; i++) addresses.add("0x9327cb34984c" + ThreadLocalRandom.current().nextInt(1000, 9999) + "ec1EA0eAE98Ccf80A74f95B9"); diff --git a/src/test/java/io/goodforgod/api/etherscan/transaction/TransactionExecApiTests.java b/src/test/java/io/goodforgod/api/etherscan/transaction/TransactionExecApiTests.java new file mode 100644 index 0000000..23e512c --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/transaction/TransactionExecApiTests.java @@ -0,0 +1,40 @@ +package io.goodforgod.api.etherscan.transaction; + +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidTxHashException; +import io.goodforgod.api.etherscan.model.Status; +import java.util.Optional; +import org.junit.jupiter.api.Test; + +/** + * @author GoodforGod + * @since 03.11.2018 + */ +class TransactionExecApiTests extends ApiRunner { + + @Test + void correct() { + Optional status = getApi().txs().statusExec("0x15f8e5ea1079d9a0bb04a4c58ae5fe7654b5b2b4463375ff7ffb490aa0032f3a"); + assertTrue(status.isPresent()); + assertTrue(status.get().haveError()); + assertNotNull(status.get().getErrDescription()); + assertNotNull(status.get().toString()); + + Status empty = Status.builder().build(); + assertNotEquals(empty, status.get()); + assertNotEquals(empty.hashCode(), status.get().hashCode()); + } + + @Test + void invalidParamWithError() { + assertThrows(EtherScanInvalidTxHashException.class, + () -> getApi().txs().statusExec("0xb513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b")); + } + + @Test + void correctParamWithEmptyExpectedResult() { + Optional status = getApi().txs().statusExec("0x55f8e5ea1079d9a0bb04a4c58ae5fe7654b5b2b4463375ff7ffb490aa0032f3a"); + assertTrue(status.isPresent()); + assertFalse(status.get().haveError()); + } +} diff --git a/src/test/java/io/goodforgod/api/etherscan/transaction/TransactionReceiptApiTests.java b/src/test/java/io/goodforgod/api/etherscan/transaction/TransactionReceiptApiTests.java new file mode 100644 index 0000000..8ff0817 --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/transaction/TransactionReceiptApiTests.java @@ -0,0 +1,34 @@ +package io.goodforgod.api.etherscan.transaction; + +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidTxHashException; +import java.util.Optional; +import org.junit.jupiter.api.Test; + +/** + * @author GoodforGod + * @since 03.11.2018 + */ +class TransactionReceiptApiTests extends ApiRunner { + + @Test + void correct() { + Optional status = getApi().txs() + .statusReceipt("0x513c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76"); + assertTrue(status.isPresent()); + assertTrue(status.get()); + } + + @Test + void invalidParamWithError() { + assertThrows(EtherScanInvalidTxHashException.class, + () -> getApi().txs().statusReceipt("0x13c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76")); + } + + @Test + void correctParamWithEmptyExpectedResult() { + Optional status = getApi().txs() + .statusReceipt("0x113c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76"); + assertFalse(status.isPresent()); + } +} diff --git a/src/test/java/io/goodforgod/api/etherscan/util/BasicUtilsTests.java b/src/test/java/io/goodforgod/api/etherscan/util/BasicUtilsTests.java new file mode 100644 index 0000000..90a2933 --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/util/BasicUtilsTests.java @@ -0,0 +1,88 @@ +package io.goodforgod.api.etherscan.util; + +import static io.goodforgod.api.etherscan.util.BasicUtils.*; + +import com.google.gson.Gson; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanResponseException; +import io.goodforgod.api.etherscan.model.response.StringResponseTO; +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.Test; + +/** + * @author GoodforGod + * @since 13.11.2018 + */ +class BasicUtilsTests extends ApiRunner { + + @Test + void responseValidateEmpty() { + String response = "{\"status\":\"0\",\"message\":\"No ether\",\"result\":\"status\"}"; + StringResponseTO responseTO = new Gson().fromJson(response, StringResponseTO.class); + + assertThrows(EtherScanResponseException.class, () -> validateTxResponse(responseTO)); + } + + @Test + void partitionEmpty() { + ArrayList list = new ArrayList<>(); + List> lists = partition(list, 12); + assertTrue(lists.isEmpty()); + } + + @Test + void partitionNullParam() { + List> lists = partition(null, 12); + assertTrue(lists.isEmpty()); + } + + @Test + void isBlankNull() { + boolean result = isBlank(null); + assertTrue(result); + } + + @Test + void isEmptyCollectionEmpty() { + ArrayList list = new ArrayList<>(); + boolean result = isEmpty(list); + assertTrue(result); + } + + @Test + void isNotAddressNull() { + boolean result = isNotAddress(""); + assertTrue(result); + } + + @Test + void isNotHexNull() { + boolean result = isNotHex(""); + assertTrue(result); + } + + @Test + void isNotAddressInvalid() { + boolean result = isNotAddress("125125"); + assertTrue(result); + } + + @Test + void isNotHexInvalid() { + boolean result = isNotHex("1215%"); + assertTrue(result); + } + + @Test + void isResponseStatusInvalidThrows() { + StringResponseTO responseTO = new StringResponseTO(); + assertThrows(EtherScanResponseException.class, () -> validateTxResponse(responseTO)); + } + + @Test + void isResponseNullThrows() { + StringResponseTO responseTO = null; + assertThrows(EtherScanResponseException.class, () -> validateTxResponse(responseTO)); + } +}