diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml
deleted file mode 100644
index 31c42f0..0000000
--- a/.github/workflows/gradle.yml
+++ /dev/null
@@ -1,51 +0,0 @@
-name: Java CI
-
-on:
- push:
- branches:
- - master
- pull_request:
- branches:
- - master
- - dev
-
-jobs:
- build:
- runs-on: ubuntu-latest
- strategy:
- matrix:
- java: [ '11', '17' ]
- name: Java ${{ matrix.java }} setup
-
- steps:
- - uses: actions/checkout@v1
- - name: Set up JDK
- uses: actions/setup-java@v1
-
- with:
- java-version: ${{ matrix.java }}
-
- - name: Build
- run: ./gradlew classes
-
- - name: Codestyle
- run: ./gradlew spotlessCheck
-
- - name: Test
- if: matrix.java == '11'
- run: ./gradlew test jacocoTestReport
- env:
- 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 }}
- SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml
new file mode 100644
index 0000000..32d0d9f
--- /dev/null
+++ b/.github/workflows/master.yml
@@ -0,0 +1,39 @@
+name: CI Master
+
+on:
+ push:
+ branches:
+ - master
+ schedule:
+ - cron: 0 0 * * 0
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ java: [ '11' ]
+ name: Master Java ${{ matrix.java }} action
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set up JDK
+ uses: actions/setup-java@v3
+ with:
+ java-version: ${{ matrix.java }}
+ distribution: 'adopt'
+
+ - name: Build
+ run: './gradlew classes'
+
+ - name: Test
+ run: './gradlew test jacocoTestReport'
+ env:
+ ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY }}
+
+ - name: SonarQube
+ if: matrix.java == '11'
+ run: './gradlew sonar --info'
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml
new file mode 100644
index 0000000..ed10cc4
--- /dev/null
+++ b/.github/workflows/publish-release.yml
@@ -0,0 +1,49 @@
+name: Release
+
+on:
+ release:
+ types: [ published ]
+
+jobs:
+ publish-release:
+ runs-on: ubuntu-latest
+ name: Publish Release
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set up JDK
+ uses: actions/setup-java@v3
+ with:
+ java-version: '17'
+ distribution: 'adopt'
+
+ - name: Build
+ run: './gradlew classes'
+
+ - name: Test
+ run: './gradlew test jacocoTestReport'
+ env:
+ ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY_PUBLISH }}
+
+ - name: SonarQube
+ run: './gradlew sonar --info'
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+
+ - name: Publish Release to GitHub Packages
+ run: './gradlew publishMavenJavaPublicationToGitHubPackagesRepository'
+ env:
+ RELEASE_VERSION: ${{ github.ref_name }}
+ GITHUB_TOKEN: ${{ secrets.OSS_GITHUB_TOKEN }}
+ ORG_GRADLE_PROJECT_signingKey: ${{ secrets.OSS_SIGNING_KEY }}
+ ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.OSS_SIGNING_PASSWORD }}
+
+ - name: Publish Release to OSSRH
+ run: './gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository'
+ env:
+ RELEASE_VERSION: ${{ github.ref_name }}
+ OSS_USERNAME: ${{ secrets.OSS_USERNAME }}
+ OSS_PASSWORD: ${{ secrets.OSS_PASSWORD }}
+ ORG_GRADLE_PROJECT_signingKey: ${{ secrets.OSS_SIGNING_KEY }}
+ ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.OSS_SIGNING_PASSWORD }}
diff --git a/.github/workflows/publish-snapshot.yml b/.github/workflows/publish-snapshot.yml
new file mode 100644
index 0000000..4a4d958
--- /dev/null
+++ b/.github/workflows/publish-snapshot.yml
@@ -0,0 +1,44 @@
+name: Snapshot
+
+on:
+ push:
+ paths:
+ - '**/workflows/*.yml'
+ - '**/java/**'
+ - '*.java'
+ - '*.gradle'
+ - '*.properties'
+ branches:
+ - dev
+
+jobs:
+ publish-snapshot:
+ runs-on: ubuntu-latest
+ name: Publish Snapshot
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set up JDK
+ uses: actions/setup-java@v3
+ with:
+ java-version: '17'
+ distribution: 'adopt'
+
+ - name: Code Style
+ run: './gradlew spotlessCheck'
+
+ - name: Build
+ run: './gradlew classes'
+
+ - name: Test
+ run: './gradlew test jacocoTestReport'
+ env:
+ ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY_PUBLISH }}
+
+ - name: Publish Snapshot
+ run: './gradlew publish'
+ env:
+ OSS_USERNAME: ${{ secrets.OSS_USERNAME }}
+ OSS_PASSWORD: ${{ secrets.OSS_PASSWORD }}
+ ORG_GRADLE_PROJECT_signingKey: ${{ secrets.OSS_SIGNING_KEY }}
+ ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.OSS_SIGNING_PASSWORD }}
diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml
new file mode 100644
index 0000000..c9c6a99
--- /dev/null
+++ b/.github/workflows/pull-request.yml
@@ -0,0 +1,41 @@
+name: CI Pull Request
+
+on:
+ pull_request:
+ branches:
+ - master
+ - dev
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ java: [ '11' ]
+ name: Pull Request Java ${{ matrix.java }} action
+
+ steps:
+ - uses: actions/checkout@v3
+ - name: Set up JDK
+ uses: actions/setup-java@v3
+ with:
+ java-version: ${{ matrix.java }}
+ distribution: 'adopt'
+
+ - name: Code Style
+ run: './gradlew spotlessCheck'
+
+ - name: Build
+ run: './gradlew classes'
+
+ - name: Test
+ run: './gradlew test jacocoTestReport'
+ env:
+ ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY }}
+
+ - name: SonarQube
+ if: matrix.java == '11'
+ run: './gradlew sonar --info'
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..5abd8dc
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,23 @@
+# Contributing Code or Documentation Guide
+
+## Running Tests
+
+The new code should contain tests that check new behavior.
+
+Run tests `./gradlew test` to check that code works as behavior.
+
+## Code Style
+
+The code base should remain clean, following industry best practices for organization, javadoc and style, as much as possible.
+
+To run the Code Style check use `./gradlew spotlessCheck`.
+
+If check found any errors, you can apply Code Style by running `./gradlew spotlessApply`
+
+## Creating a pull request
+
+Once you are satisfied with your changes:
+
+- Commit changes to the local branch you created.
+- Push that branch with changes to the corresponding remote branch on GitHub
+- Submit a [pull request](https://help.github.com/articles/creating-a-pull-request) to `dev` branch.
\ No newline at end of file
diff --git a/README.md b/README.md
index dd244b5..c09c885 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,8 @@
# Java EtherScan API
-[](https://openjdk.org/projects/jdk8/)
-[](https://github.com/GoodforGod/java-etherscan-api/actions?query=workflow%3A%22Java+CI%22)
+[](https://openjdk.org/projects/jdk/11/)
+[](https://maven-badges.herokuapp.com/maven-central/com.github.goodforgod/java-etherscan-api)
+[](https://github.com/GoodforGod/java-etherscan-api/actions?query=workflow%3ACI+Master)
[](https://sonarcloud.io/dashboard?id=GoodforGod_java-etherscan-api)
[](https://sonarcloud.io/dashboard?id=GoodforGod_java-etherscan-api)
[](https://sonarcloud.io/dashboard?id=GoodforGod_java-etherscan-api)
@@ -14,7 +15,7 @@ Library supports EtherScan *API* for all available *Ethereum Networks* for *ethe
**Gradle**
```groovy
-implementation "com.github.goodforgod:java-etherscan-api:2.0.0"
+implementation "com.github.goodforgod:java-etherscan-api:3.0.0"
```
**Maven**
@@ -22,7 +23,7 @@ implementation "com.github.goodforgod:java-etherscan-api:2.0.0"
com.github.goodforgod
java-etherscan-api
- 2.0.0
+ 3.0.0
```
@@ -48,7 +49,7 @@ API support all Ethereum [default networks](https://docs.etherscan.io/getting-st
- [Sepolia](https://api-sepolia.etherscan.io/)
```java
-EtherScanAPI api = EtherScanAPI.build();
+EtherScanAPI api = EtherScanAPI.builder().build();
EtherScanAPI apiGoerli = EtherScanAPI.builder().withNetwork(EthNetworks.GORLI).build();
EtherScanAPI apiSepolia = EtherScanAPI.builder().withNetwork(EthNetworks.SEPOLIA).build();
```
@@ -75,6 +76,14 @@ EtherScanAPI api = EtherScanAPI.builder()
.build();
```
+Also you can use Java 11+ HttpClient:
+```java
+Supplier ethHttpClientSupplier = () -> new JdkEthHttpClient();
+EtherScanAPI api = EtherScanAPI.builder()
+ .withHttpClient(supplier)
+ .build();
+```
+
## API Examples
You can read about all API methods on [Etherscan](https://docs.etherscan.io/api-endpoints/accounts)
@@ -96,7 +105,7 @@ Below are examples for each API category.
**Get Ether Balance for a single Address**
```java
-EtherScanAPI api = EtherScanAPI.build();
+EtherScanAPI api = EtherScanAPI.builder().build();
Balance balance = api.account().balance("0x8d4426f94e42f721C7116E81d6688cd935cB3b4F");
```
@@ -104,14 +113,14 @@ Balance balance = api.account().balance("0x8d4426f94e42f721C7116E81d6688cd935cB3
**Get uncles block for block height**
```java
-EtherScanAPI api = EtherScanAPI.build();
+EtherScanAPI api = EtherScanAPI.builder().build();
Optional uncles = api.block().uncles(200000);
```
### Contract API
**Request contract ABI from [verified codes](https://etherscan.io/contractsVerified)**
```java
-EtherScanAPI api = EtherScanAPI.build();
+EtherScanAPI api = EtherScanAPI.builder().build();
Abi abi = api.contract().contractAbi("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413");
```
@@ -119,7 +128,7 @@ Abi abi = api.contract().contractAbi("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413
**Get event logs for single topic**
```java
-EtherScanAPI api = EtherScanAPI.build();
+EtherScanAPI api = EtherScanAPI.builder().build();
LogQuery query = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c")
.withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545")
.build();
@@ -128,7 +137,7 @@ List logs = api.logs().logs(query);
**Get event logs for 3 topics with respectful operations**
```java
-EtherScanAPI api = EtherScanAPI.build();
+EtherScanAPI api = EtherScanAPI.builder().build();
LogQuery query = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c")
.withBlockFrom(379224)
.withBlockTo(400000)
@@ -147,13 +156,13 @@ List logs = api.logs().logs(query);
**Get tx details with proxy endpoint**
```java
-EtherScanAPI api = EtherScanAPI.build();
-Optional tx = api.proxy().tx("0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1");
+EtherScanAPI api = EtherScanAPI.builder().build();
+Optional tx = api.proxy().tx("0x1e2910a263.0.08d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1");
```
**Get block info with proxy endpoint**
```java
-EtherScanAPI api = EtherScanAPI.build();
+EtherScanAPI api = EtherScanAPI.builder().build();
Optional block = api.proxy().block(15215);
```
@@ -161,7 +170,7 @@ Optional block = api.proxy().block(15215);
**Statistic about last price**
```java
-EtherScanAPI api = EtherScanAPI.build();
+EtherScanAPI api = EtherScanAPI.builder().build();
Price price = api.stats().priceLast();
```
@@ -169,7 +178,7 @@ Price price = api.stats().priceLast();
**Request receipt status for tx**
```java
-EtherScanAPI api = EtherScanAPI.build();
+EtherScanAPI api = EtherScanAPI.builder().build();
Optional status = api.txs().receiptStatus("0x513c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76");
```
diff --git a/_config.yml b/_config.yml
deleted file mode 100644
index 2f7efbe..0000000
--- a/_config.yml
+++ /dev/null
@@ -1 +0,0 @@
-theme: jekyll-theme-minimal
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index 3d766c2..b9be8cd 100644
--- a/build.gradle
+++ b/build.gradle
@@ -3,8 +3,9 @@ plugins {
id "java-library"
id "maven-publish"
- id "org.sonarqube" version "3.3"
- id "com.diffplug.spotless" version "6.12.0"
+ id "org.sonarqube" version "6.3.1.5724"
+ id "com.diffplug.spotless" version "6.19.0"
+ id "io.github.gradle-nexus.publish-plugin" version "2.0.0"
}
repositories {
@@ -13,21 +14,23 @@ repositories {
}
group = groupId
-version = artifactVersion
+var ver = System.getenv().getOrDefault("RELEASE_VERSION", artifactVersion)
+version = ver.startsWith("v") ? ver.substring(1) : ver
-sourceCompatibility = JavaVersion.VERSION_1_8
-targetCompatibility = JavaVersion.VERSION_1_8
+sourceCompatibility = JavaVersion.VERSION_11
+targetCompatibility = JavaVersion.VERSION_11
dependencies {
- compileOnly "org.jetbrains:annotations:23.0.0"
+ compileOnly "org.jetbrains:annotations:24.0.1"
implementation "io.goodforgod:gson-configuration:2.0.0"
- 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"
+ testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.11.4"
+ testImplementation "org.junit.jupiter:junit-jupiter-api:5.11.4"
+ testImplementation "org.junit.jupiter:junit-jupiter-params:5.11.4"
}
test {
+ failFast(false)
useJUnitPlatform()
testLogging {
events("passed", "skipped", "failed")
@@ -36,9 +39,13 @@ test {
}
reports {
- html.enabled(false)
- junitXml.enabled(false)
+ html.required = false
+ junitXml.required = true
}
+
+ environment([
+ "": "",
+ ])
}
spotless {
@@ -46,7 +53,7 @@ spotless {
encoding("UTF-8")
importOrder()
removeUnusedImports()
- eclipse("4.21.0").configFile("${rootDir}/config/codestyle.xml")
+ eclipse("4.21").configFile("${rootDir}/config/codestyle.xml")
}
}
@@ -58,6 +65,18 @@ sonarqube {
}
}
+nexusPublishing {
+ packageGroup = groupId
+ repositories {
+ sonatype {
+ username = System.getenv("OSS_USERNAME")
+ password = System.getenv("OSS_PASSWORD")
+ nexusUrl.set(uri("https://ossrh-staging-api.central.sonatype.com/service/local/"))
+ snapshotRepositoryUrl.set(uri("https://central.sonatype.com/repository/maven-snapshots/"))
+ }
+ }
+}
+
publishing {
publications {
mavenJava(MavenPublication) {
@@ -91,14 +110,24 @@ publishing {
}
repositories {
maven {
- def releasesRepoUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2"
- def snapshotsRepoUrl = "https://oss.sonatype.org/content/repositories/snapshots/"
+ def releasesRepoUrl = "https://ossrh-staging-api.central.sonatype.com/service/local/"
+ def snapshotsRepoUrl = "https://central.sonatype.com/repository/maven-snapshots/"
url = version.endsWith("SNAPSHOT") ? snapshotsRepoUrl : releasesRepoUrl
credentials {
username System.getenv("OSS_USERNAME")
password System.getenv("OSS_PASSWORD")
}
}
+ if (!version.endsWith("SNAPSHOT")) {
+ maven {
+ name = "GitHubPackages"
+ url = "https://maven.pkg.github.com/GoodforGod/$artifactId"
+ credentials {
+ username = System.getenv("GITHUB_ACTOR")
+ password = System.getenv("GITHUB_TOKEN")
+ }
+ }
+ }
}
}
@@ -116,7 +145,7 @@ tasks.withType(JavaCompile) {
check.dependsOn jacocoTestReport
jacocoTestReport {
reports {
- xml.enabled true
+ xml.required = true
html.destination file("${buildDir}/jacocoHtml")
}
}
@@ -128,9 +157,12 @@ javadoc {
}
}
-if (project.hasProperty("signing.keyId")) {
+if (project.hasProperty("signingKey")) {
apply plugin: "signing"
signing {
+ def signingKey = findProperty("signingKey")
+ def signingPassword = findProperty("signingPassword")
+ useInMemoryPgpKeys(signingKey, signingPassword)
sign publishing.publications.mavenJava
}
}
diff --git a/gradle.properties b/gradle.properties
index 821da06..7b97567 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,6 +1,6 @@
groupId=com.github.goodforgod
artifactId=java-etherscan-api
-artifactVersion=2.0.0
+artifactVersion=3.0.0-SNAPSHOT
##### GRADLE #####
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 7454180..1b33c55 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 070cb70..d4081da 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index 1b6c787..23d15a9 100755
--- a/gradlew
+++ b/gradlew
@@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
+# SPDX-License-Identifier: Apache-2.0
+#
##############################################################################
#
@@ -55,7 +57,7 @@
# 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
+# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -80,13 +82,11 @@ do
esac
done
-APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
-
-APP_NAME="Gradle"
+# This is normally unused
+# shellcheck disable=SC2034
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"'
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -114,7 +114,7 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM.
@@ -133,22 +133,29 @@ location of your Java installation."
fi
else
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.
+ if ! command -v java >/dev/null 2>&1
+ then
+ 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
location of your Java installation."
+ fi
fi
# Increase the maximum file descriptors if we can.
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@@ -193,18 +200,28 @@ if "$cygwin" || "$msys" ; then
done
fi
-# 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.
+
+# 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"'
+
+# Collect all arguments for the java command:
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+# and any embedded shellness will be escaped.
+# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
- org.gradle.wrapper.GradleWrapperMain \
+ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
diff --git a/gradlew.bat b/gradlew.bat
index 107acd3..db3a6ac 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -13,8 +13,10 @@
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
+@rem SPDX-License-Identifier: Apache-2.0
+@rem
-@if "%DEBUG%" == "" @echo off
+@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@@ -25,7 +27,8 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto execute
+if %ERRORLEVEL% equ 0 goto execute
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
goto fail
@@ -56,32 +59,34 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+set CLASSPATH=
@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
+if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
diff --git a/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java
index 442edff..aceb9a2 100644
--- a/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java
+++ b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java
@@ -21,9 +21,9 @@
* @author GoodforGod
* @since 28.10.2018
*/
-final class AccountAPIProvider extends BasicProvider implements AccountAPI {
+public class AccountAPIProvider extends BasicProvider implements AccountAPI {
- private static final int OFFSET_MAX = 10000;
+ private static final int OFFSET_MAX = 9999;
private static final String ACT_BALANCE_ACTION = ACT_PREFIX + "balance";
private static final String ACT_TOKEN_BALANCE_PARAM = ACT_PREFIX + "tokenbalance";
@@ -47,11 +47,12 @@ final class AccountAPIProvider extends BasicProvider implements AccountAPI {
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);
+ public AccountAPIProvider(RequestQueueManager requestQueueManager,
+ String baseUrl,
+ EthHttpClient executor,
+ Converter converter,
+ int retryCount) {
+ super(requestQueueManager, "account", baseUrl, executor, converter, retryCount);
}
@NotNull
@@ -60,7 +61,7 @@ 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);
+ final StringResponseTO response = getResponse(urlParams, StringResponseTO.class);
if (response.getStatus() != 1)
throw new EtherScanResponseException(response);
@@ -74,7 +75,7 @@ public TokenBalance balance(@NotNull String address, @NotNull String contract) t
BasicUtils.validateAddress(contract);
final String urlParams = ACT_TOKEN_BALANCE_PARAM + ADDRESS_PARAM + address + CONTRACT_PARAM + contract;
- final StringResponseTO response = getRequest(urlParams, StringResponseTO.class);
+ final StringResponseTO response = getResponse(urlParams, StringResponseTO.class);
if (response.getStatus() != 1)
throw new EtherScanResponseException(response);
@@ -95,8 +96,9 @@ public List balances(@NotNull List addresses) throws EtherScanE
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);
+ final String urlParams = ACT_BALANCE_MULTI_ACTION + TAG_LATEST_PARAM + ADDRESS_PARAM
+ + BasicUtils.toAddressParam(batch);
+ final BalanceResponseTO response = getResponse(urlParams, BalanceResponseTO.class);
if (response.getStatus() != 1) {
throw new EtherScanResponseException(response);
}
@@ -111,10 +113,6 @@ public List balances(@NotNull List addresses) throws EtherScanE
return balances;
}
- private String toAddressParam(List addresses) {
- return String.join(",", addresses);
- }
-
@NotNull
@Override
public List txs(@NotNull String address) throws EtherScanException {
@@ -141,34 +139,6 @@ public List txs(@NotNull String address, long startBlock, long endBlock) thr
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 {
@@ -202,7 +172,7 @@ public List txsInternalByHash(@NotNull String txhash) throws EtherSc
BasicUtils.validateTxHash(txhash);
final String urlParams = ACT_TX_INTERNAL_ACTION + TXHASH_PARAM + txhash;
- final TxInternalResponseTO response = getRequest(urlParams, TxInternalResponseTO.class);
+ final TxInternalResponseTO response = getResponse(urlParams, TxInternalResponseTO.class);
BasicUtils.validateTxResponse(response);
return BasicUtils.isEmpty(response.getResult())
diff --git a/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java b/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java
index 5c61aad..0ab4f20 100644
--- a/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java
+++ b/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java
@@ -1,13 +1,20 @@
package io.goodforgod.api.etherscan;
+import io.goodforgod.api.etherscan.error.EtherScanException;
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.http.EthResponse;
import io.goodforgod.api.etherscan.manager.RequestQueueManager;
+import io.goodforgod.api.etherscan.model.response.BaseListResponseTO;
import io.goodforgod.api.etherscan.model.response.StringResponseTO;
+import io.goodforgod.api.etherscan.util.BasicUtils;
import java.net.URI;
import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import org.jetbrains.annotations.ApiStatus.Internal;
/**
* Base provider for API Implementations
@@ -16,10 +23,13 @@
* @see EtherScanAPIProvider
* @since 28.10.2018
*/
-abstract class BasicProvider {
+@Internal
+public abstract class BasicProvider {
private static final String MAX_RATE_LIMIT_REACHED = "Max rate limit reached";
+ private static final int OFFSET_MAX = 9999;
+
static final int MAX_END_BLOCK = Integer.MAX_VALUE;
static final int MIN_START_BLOCK = 0;
@@ -30,20 +40,23 @@ abstract class BasicProvider {
private final EthHttpClient executor;
private final RequestQueueManager queue;
private final Converter converter;
+ private final int retryCountLimit;
- BasicProvider(RequestQueueManager requestQueueManager,
- String module,
- String baseUrl,
- EthHttpClient ethHttpClient,
- Converter converter) {
+ public BasicProvider(RequestQueueManager requestQueueManager,
+ String module,
+ String baseUrl,
+ EthHttpClient ethHttpClient,
+ Converter converter,
+ int retryCountLimit) {
this.queue = requestQueueManager;
this.module = "&module=" + module;
this.baseUrl = baseUrl;
this.executor = ethHttpClient;
this.converter = converter;
+ this.retryCountLimit = retryCountLimit;
}
- T convert(byte[] json, Class tClass) {
+ protected 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)) {
@@ -66,23 +79,93 @@ T convert(byte[] json, Class tClass) {
}
}
- byte[] getRequest(String urlParameters) {
+ protected int getMaximumOffset() {
+ return OFFSET_MAX;
+ }
+
+ /**
+ * 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
+ */
+ protected > 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 = getResponse(formattedUrl, tClass);
+ BasicUtils.validateTxResponse(response);
+ if (BasicUtils.isEmpty(response.getResult()))
+ break;
+
+ result.addAll(response.getResult());
+ if (response.getResult().size() < getMaximumOffset())
+ break;
+ }
+
+ return result;
+ }
+
+ protected EthResponse getResponse(String urlParameters) {
queue.takeTurn();
final URI uri = URI.create(baseUrl + module + urlParameters);
return executor.get(uri);
}
- byte[] postRequest(String urlParameters, String dataToPost) {
+ protected EthResponse 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);
+ protected T getResponse(String urlParameters, Class tClass) {
+ return getResponse(urlParameters, tClass, 0);
}
- T postRequest(String urlParameters, String dataToPost, Class tClass) {
- return convert(postRequest(urlParameters, dataToPost), tClass);
+ protected T getResponse(String urlParameters, Class tClass, int retryCount) {
+ try {
+ EthResponse response = getResponse(urlParameters);
+ return convert(response.body(), tClass);
+ } catch (Exception e) {
+ if (retryCount < retryCountLimit) {
+ try {
+ Thread.sleep(1150);
+ } catch (InterruptedException ex) {
+ throw new IllegalStateException(ex);
+ }
+
+ return getResponse(urlParameters, tClass, retryCount + 1);
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ protected T postRequest(String urlParameters, String dataToPost, Class tClass) {
+ return postRequest(urlParameters, dataToPost, tClass, 0);
+ }
+
+ protected T postRequest(String urlParameters, String dataToPost, Class tClass, int retryCount) {
+ try {
+ EthResponse response = postRequest(urlParameters, dataToPost);
+ return convert(response.body(), tClass);
+ } catch (EtherScanRateLimitException e) {
+ if (retryCount < retryCountLimit) {
+ try {
+ Thread.sleep(1150);
+ } catch (InterruptedException ex) {
+ throw new IllegalStateException(ex);
+ }
+
+ return postRequest(urlParameters, dataToPost, tClass, retryCount + 1);
+ } else {
+ throw e;
+ }
+ }
}
}
diff --git a/src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java
index 406ac19..7228943 100644
--- a/src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java
+++ b/src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java
@@ -17,30 +17,26 @@
* @see BlockAPI
* @since 28.10.2018
*/
-final class BlockAPIProvider extends BasicProvider implements BlockAPI {
+public 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);
+ public BlockAPIProvider(RequestQueueManager requestQueueManager,
+ String baseUrl,
+ EthHttpClient executor,
+ Converter converter,
+ int retryCount) {
+ super(requestQueueManager, "block", baseUrl, executor, converter, retryCount);
}
@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);
+ final UncleBlockResponseTO responseTO = getResponse(urlParam, UncleBlockResponseTO.class);
if (responseTO.getMessage().startsWith("NOTOK")) {
return Optional.empty();
}
diff --git a/src/main/java/io/goodforgod/api/etherscan/ContractAPI.java b/src/main/java/io/goodforgod/api/etherscan/ContractAPI.java
index af0852c..c076b74 100644
--- a/src/main/java/io/goodforgod/api/etherscan/ContractAPI.java
+++ b/src/main/java/io/goodforgod/api/etherscan/ContractAPI.java
@@ -2,6 +2,8 @@
import io.goodforgod.api.etherscan.error.EtherScanException;
import io.goodforgod.api.etherscan.model.Abi;
+import io.goodforgod.api.etherscan.model.ContractCreation;
+import java.util.List;
import org.jetbrains.annotations.NotNull;
/**
@@ -21,4 +23,13 @@ public interface ContractAPI {
*/
@NotNull
Abi contractAbi(@NotNull String address) throws EtherScanException;
+
+ /**
+ * Returns a contract's deployer address and transaction hash it was created, up to 5 at a time.
+ *
+ * @param contractAddresses - list of addresses to fetch
+ * @throws EtherScanException parent exception class
+ */
+ @NotNull
+ List contractCreation(@NotNull List contractAddresses) throws EtherScanException;
}
diff --git a/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java
index 6b4404a..0719569 100644
--- a/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java
+++ b/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java
@@ -5,8 +5,12 @@
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.ContractCreation;
+import io.goodforgod.api.etherscan.model.response.ContractCreationResponseTO;
import io.goodforgod.api.etherscan.model.response.StringResponseTO;
import io.goodforgod.api.etherscan.util.BasicUtils;
+import java.util.List;
+import java.util.stream.Collectors;
import org.jetbrains.annotations.NotNull;
/**
@@ -16,17 +20,24 @@
* @author GoodforGod
* @since 28.10.2018
*/
-final class ContractAPIProvider extends BasicProvider implements ContractAPI {
+public 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);
+ private static final String ACT_CONTRACT_CREATION_PARAM = "getcontractcreation";
+
+ private static final String ACT_CONTRACT_CREATION = ACT_PREFIX + ACT_CONTRACT_CREATION_PARAM;
+
+ private static final String ACT_CONTRACT_ADDRESSES_PARAM = "&contractaddresses=";
+
+ public ContractAPIProvider(RequestQueueManager requestQueueManager,
+ String baseUrl,
+ EthHttpClient executor,
+ Converter converter,
+ int retryCount) {
+ super(requestQueueManager, "contract", baseUrl, executor, converter, retryCount);
}
@NotNull
@@ -35,7 +46,7 @@ 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);
+ final StringResponseTO response = getResponse(urlParam, StringResponseTO.class);
if (response.getStatus() != 1 && response.getMessage().startsWith("NOTOK")) {
throw new EtherScanResponseException(response);
}
@@ -44,4 +55,24 @@ public Abi contractAbi(@NotNull String address) throws EtherScanException {
? Abi.nonVerified()
: Abi.verified(response.getResult());
}
+
+ @NotNull
+ @Override
+ public List contractCreation(@NotNull List contractAddresses) throws EtherScanException {
+ BasicUtils.validateAddresses(contractAddresses);
+ final String urlParam = ACT_CONTRACT_CREATION + ACT_CONTRACT_ADDRESSES_PARAM
+ + BasicUtils.toAddressParam(contractAddresses);
+ final ContractCreationResponseTO response = getResponse(urlParam, ContractCreationResponseTO.class);
+ if (response.getStatus() != 1 && response.getMessage().startsWith("NOTOK")) {
+ throw new EtherScanResponseException(response);
+ }
+
+ return response.getResult().stream()
+ .map(to -> ContractCreation.builder()
+ .withContractCreator(to.getContractCreator())
+ .withContractAddress(to.getContractAddress())
+ .withTxHash(to.getTxHash())
+ .build())
+ .collect(Collectors.toList());
+ }
}
diff --git a/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java b/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java
index dad9c50..fa8af66 100644
--- a/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java
+++ b/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java
@@ -4,7 +4,7 @@
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.http.impl.JdkEthHttpClient;
import io.goodforgod.api.etherscan.manager.RequestQueueManager;
import io.goodforgod.api.etherscan.util.BasicUtils;
import io.goodforgod.gson.configuration.GsonConfiguration;
@@ -12,6 +12,7 @@
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
+import java.util.Objects;
import java.util.function.Supplier;
import org.jetbrains.annotations.NotNull;
@@ -19,14 +20,14 @@
* @author Anton Kurako (GoodforGod)
* @since 11.05.2023
*/
-final class EthScanAPIBuilder implements EtherScanAPI.Builder {
+public class EthScanAPIBuilder implements EtherScanAPI.Builder {
- private static final Supplier DEFAULT_SUPPLIER = UrlEthHttpClient::new;
- private static final String DEFAULT_KEY = "YourApiKeyToken";
+ private static final Supplier DEFAULT_SUPPLIER = JdkEthHttpClient::new;
private final Gson gson = new GsonConfiguration().builder().create();
- private String apiKey = DEFAULT_KEY;
+ private int retryCountOnLimitReach = 0;
+ private String apiKey;
private RequestQueueManager queueManager;
private EthNetwork ethNetwork = EthNetworks.MAINNET;
private Supplier ethHttpClientSupplier = DEFAULT_SUPPLIER;
@@ -42,6 +43,10 @@ final class EthScanAPIBuilder implements EtherScanAPI.Builder {
}
};
+ public EthScanAPIBuilder(String apiKey) {
+ this.apiKey = apiKey;
+ }
+
@NotNull
@Override
public EtherScanAPI.Builder withApiKey(@NotNull String apiKey) {
@@ -87,18 +92,22 @@ public EtherScanAPI.Builder withConverter(@NotNull Supplier converter
return this;
}
+ @NotNull
+ public EtherScanAPI.Builder withRetryOnRateLimit(int maxRetryCount) {
+ if (maxRetryCount < 0 || maxRetryCount > 20) {
+ throw new IllegalStateException("maxRetryCount value must be in range from 0 to 20, but was: " + maxRetryCount);
+ }
+
+ this.retryCountOnLimitReach = maxRetryCount;
+ 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();
- }
+ requestQueueManager = Objects.requireNonNullElseGet(queueManager, RequestQueueManager::planFree);
return new EtherScanAPIProvider(apiKey, ethNetwork, requestQueueManager, ethHttpClientSupplier.get(),
- converterSupplier.get());
+ converterSupplier.get(), retryCountOnLimitReach);
}
}
diff --git a/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java
index 6da3d8f..4c703bb 100644
--- a/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java
+++ b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java
@@ -1,9 +1,11 @@
package io.goodforgod.api.etherscan;
+import io.goodforgod.api.etherscan.error.EtherScanRateLimitException;
import io.goodforgod.api.etherscan.http.EthHttpClient;
import io.goodforgod.api.etherscan.manager.RequestQueueManager;
import java.util.function.Supplier;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Range;
/**
* EtherScan full API Description ...
@@ -38,8 +40,8 @@ public interface EtherScanAPI extends AutoCloseable {
GasTrackerAPI gasTracker();
@NotNull
- static Builder builder() {
- return new EthScanAPIBuilder();
+ static Builder builder(@NotNull String key) {
+ return new EthScanAPIBuilder(key);
}
interface Builder {
@@ -62,6 +64,15 @@ interface Builder {
@NotNull
Builder withConverter(@NotNull Supplier converterSupplier);
+ /**
+ * By default is disabled
+ *
+ * @param maxRetryCount to retry if {@link EtherScanRateLimitException} thrown
+ * @return self
+ */
+ @NotNull
+ EtherScanAPI.Builder withRetryOnRateLimit(@Range(from = 0, to = 20) int maxRetryCount);
+
@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
index e698f45..daf1e4a 100644
--- a/src/main/java/io/goodforgod/api/etherscan/EtherScanAPIProvider.java
+++ b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPIProvider.java
@@ -10,7 +10,7 @@
* @author GoodforGod
* @since 28.10.2018
*/
-final class EtherScanAPIProvider implements EtherScanAPI {
+public class EtherScanAPIProvider implements EtherScanAPI {
private final RequestQueueManager requestQueueManager;
private final AccountAPI account;
@@ -22,23 +22,24 @@ final class EtherScanAPIProvider implements EtherScanAPI {
private final TransactionAPI txs;
private final GasTrackerAPI gasTracker;
- EtherScanAPIProvider(String apiKey,
- EthNetwork network,
- RequestQueueManager queue,
- EthHttpClient ethHttpClient,
- Converter converter) {
+ public EtherScanAPIProvider(String apiKey,
+ EthNetwork network,
+ RequestQueueManager queue,
+ EthHttpClient ethHttpClient,
+ Converter converter,
+ int retryCount) {
// 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);
+ this.account = new AccountAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
+ this.block = new BlockAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
+ this.contract = new ContractAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
+ this.logs = new LogsAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
+ this.proxy = new ProxyAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
+ this.stats = new StatisticAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
+ this.txs = new TransactionAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
+ this.gasTracker = new GasTrackerAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
}
@NotNull
diff --git a/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java
index cbe0a75..e7ecb7c 100644
--- a/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java
+++ b/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java
@@ -18,24 +18,25 @@
* @author Abhay Gupta
* @since 14.11.2022
*/
-final class GasTrackerAPIProvider extends BasicProvider implements GasTrackerAPI {
+public 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);
+ public GasTrackerAPIProvider(RequestQueueManager queue,
+ String baseUrl,
+ EthHttpClient ethHttpClient,
+ Converter converter,
+ int retryCount) {
+ super(queue, "gastracker", baseUrl, ethHttpClient, converter, retryCount);
}
@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);
+ final GasEstimateResponseTO response = getResponse(urlParams, GasEstimateResponseTO.class);
if (response.getStatus() != 1)
throw new EtherScanResponseException(response);
@@ -45,7 +46,7 @@ final class GasTrackerAPIProvider extends BasicProvider implements GasTrackerAPI
@NotNull
@Override
public GasOracle oracle() throws EtherScanException {
- final GasOracleResponseTO response = getRequest(ACT_GAS_ORACLE_PARAM, GasOracleResponseTO.class);
+ final GasOracleResponseTO response = getResponse(ACT_GAS_ORACLE_PARAM, GasOracleResponseTO.class);
if (response.getStatus() != 1)
throw new EtherScanResponseException(response);
diff --git a/src/main/java/io/goodforgod/api/etherscan/LogsAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/LogsAPIProvider.java
index d294fb5..1002dc8 100644
--- a/src/main/java/io/goodforgod/api/etherscan/LogsAPIProvider.java
+++ b/src/main/java/io/goodforgod/api/etherscan/LogsAPIProvider.java
@@ -18,22 +18,23 @@
* @author GoodforGod
* @since 28.10.2018
*/
-final class LogsAPIProvider extends BasicProvider implements LogsAPI {
+public 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);
+ public LogsAPIProvider(RequestQueueManager queue,
+ String baseUrl,
+ EthHttpClient executor,
+ Converter converter,
+ int retryCount) {
+ super(queue, "logs", baseUrl, executor, converter, retryCount);
}
@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);
+ final LogResponseTO response = getResponse(urlParams, LogResponseTO.class);
BasicUtils.validateTxResponse(response);
return (BasicUtils.isEmpty(response.getResult()))
diff --git a/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java
index 4dff589..e35fd08 100644
--- a/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java
+++ b/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java
@@ -26,7 +26,7 @@
* @author GoodforGod
* @since 28.10.2018
*/
-final class ProxyAPIProvider extends BasicProvider implements ProxyAPI {
+public 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";
@@ -57,16 +57,17 @@ final class ProxyAPIProvider extends BasicProvider implements ProxyAPI {
private static final Pattern EMPTY_HEX = Pattern.compile("0x0+");
- ProxyAPIProvider(RequestQueueManager queue,
- String baseUrl,
- EthHttpClient executor,
- Converter converter) {
- super(queue, "proxy", baseUrl, executor, converter);
+ public ProxyAPIProvider(RequestQueueManager queue,
+ String baseUrl,
+ EthHttpClient executor,
+ Converter converter,
+ int retryCount) {
+ super(queue, "proxy", baseUrl, executor, converter, retryCount);
}
@Override
public long blockNoLast() throws EtherScanException {
- final StringProxyTO response = getRequest(ACT_BLOCKNO_PARAM, StringProxyTO.class);
+ final StringProxyTO response = getResponse(ACT_BLOCKNO_PARAM, StringProxyTO.class);
return (BasicUtils.isEmpty(response.getResult()))
? -1
: BasicUtils.parseHex(response.getResult()).longValue();
@@ -78,7 +79,7 @@ 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;
- final BlockProxyTO response = getRequest(urlParams, BlockProxyTO.class);
+ final BlockProxyTO response = getResponse(urlParams, BlockProxyTO.class);
return Optional.ofNullable(response.getResult());
}
@@ -90,7 +91,7 @@ public Optional blockUncle(long blockNo, long index) throws EtherSca
final String urlParams = ACT_UNCLE_BY_BLOCKNOINDEX_PARAM + TAG_PARAM
+ "0x" + Long.toHexString(compBlockNo) + INDEX_PARAM + "0x" + Long.toHexString(compIndex);
- final BlockProxyTO response = getRequest(urlParams, BlockProxyTO.class);
+ final BlockProxyTO response = getResponse(urlParams, BlockProxyTO.class);
return Optional.ofNullable(response.getResult());
}
@@ -100,7 +101,7 @@ public Optional tx(@NotNull String txhash) throws EtherScanException {
BasicUtils.validateTxHash(txhash);
final String urlParams = ACT_TX_BY_HASH_PARAM + TXHASH_PARAM + txhash;
- final TxProxyTO response = getRequest(urlParams, TxProxyTO.class);
+ final TxProxyTO response = getResponse(urlParams, TxProxyTO.class);
return Optional.ofNullable(response.getResult());
}
@@ -114,7 +115,7 @@ public Optional tx(long blockNo, long index) throws EtherScanException
final String urlParams = ACT_TX_BY_BLOCKNOINDEX_PARAM + TAG_PARAM + compBlockNo + INDEX_PARAM + "0x"
+ Long.toHexString(compIndex);
- final TxProxyTO response = getRequest(urlParams, TxProxyTO.class);
+ final TxProxyTO response = getResponse(urlParams, TxProxyTO.class);
return Optional.ofNullable(response.getResult());
}
@@ -122,7 +123,7 @@ public Optional tx(long blockNo, long index) throws EtherScanException
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);
+ final StringProxyTO response = getResponse(urlParams, StringProxyTO.class);
return BasicUtils.parseHex(response.getResult()).intValue();
}
@@ -131,7 +132,7 @@ public int txSendCount(@NotNull String address) throws EtherScanException {
BasicUtils.validateAddress(address);
final String urlParams = ACT_TX_COUNT_PARAM + ADDRESS_PARAM + address + TAG_LAST_PARAM;
- final StringProxyTO response = getRequest(urlParams, StringProxyTO.class);
+ final StringProxyTO response = getResponse(urlParams, StringProxyTO.class);
return BasicUtils.parseHex(response.getResult()).intValue();
}
@@ -164,7 +165,7 @@ public Optional txReceipt(@NotNull String txhash) throws EtherScan
BasicUtils.validateTxHash(txhash);
final String urlParams = ACT_TX_RECEIPT_PARAM + TXHASH_PARAM + txhash;
- final TxInfoProxyTO response = getRequest(urlParams, TxInfoProxyTO.class);
+ final TxInfoProxyTO response = getResponse(urlParams, TxInfoProxyTO.class);
return Optional.ofNullable(response.getResult());
}
@@ -176,7 +177,7 @@ public Optional call(@NotNull String address, @NotNull String data) thro
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);
+ final StringProxyTO response = getResponse(urlParams, StringProxyTO.class);
return Optional.ofNullable(response.getResult());
}
@@ -186,7 +187,7 @@ public Optional code(@NotNull String address) throws EtherScanException
BasicUtils.validateAddress(address);
final String urlParams = ACT_CODE_PARAM + ADDRESS_PARAM + address + TAG_LAST_PARAM;
- final StringProxyTO response = getRequest(urlParams, StringProxyTO.class);
+ final StringProxyTO response = getResponse(urlParams, StringProxyTO.class);
return Optional.ofNullable(response.getResult());
}
@@ -197,7 +198,7 @@ public Optional storageAt(@NotNull String address, long position) throws
final long compPosition = BasicUtils.compensateMinBlock(position);
final String urlParams = ACT_STORAGEAT_PARAM + ADDRESS_PARAM + address + POSITION_PARAM + compPosition + TAG_LAST_PARAM;
- final StringProxyTO response = getRequest(urlParams, StringProxyTO.class);
+ final StringProxyTO response = getResponse(urlParams, StringProxyTO.class);
return (BasicUtils.isEmpty(response.getResult()) || EMPTY_HEX.matcher(response.getResult()).matches())
? Optional.empty()
: Optional.of(response.getResult());
@@ -206,7 +207,7 @@ public Optional storageAt(@NotNull String address, long position) throws
@NotNull
@Override
public Wei gasPrice() throws EtherScanException {
- final StringProxyTO response = getRequest(ACT_GASPRICE_PARAM, StringProxyTO.class);
+ final StringProxyTO response = getResponse(ACT_GASPRICE_PARAM, StringProxyTO.class);
return (BasicUtils.isEmpty(response.getResult()))
? Wei.ofWei(0)
: Wei.ofWei(BasicUtils.parseHex(response.getResult()));
@@ -225,7 +226,7 @@ public Wei gasEstimated(@NotNull String hexData) throws EtherScanException {
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);
+ final StringProxyTO response = getResponse(urlParams, StringProxyTO.class);
return (BasicUtils.isEmpty(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
index d7b48b8..3f48127 100644
--- a/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java
+++ b/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java
@@ -15,6 +15,9 @@
public interface StatisticAPI {
/**
+ * ERC20 token total Supply
+ * EtherScan
* Returns the current amount of an ERC-20 token in circulation.
*
* @param contract contract address
diff --git a/src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java
index 131df71..006017a 100644
--- a/src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java
+++ b/src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java
@@ -21,7 +21,7 @@
* @author GoodforGod
* @since 28.10.2018
*/
-final class StatisticAPIProvider extends BasicProvider implements StatisticAPI {
+public 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";
@@ -30,17 +30,18 @@ final class StatisticAPIProvider extends BasicProvider implements StatisticAPI {
private static final String CONTRACT_ADDRESS_PARAM = "&contractaddress=";
- StatisticAPIProvider(RequestQueueManager queue,
- String baseUrl,
- EthHttpClient executor,
- Converter converter) {
- super(queue, "stats", baseUrl, executor, converter);
+ public StatisticAPIProvider(RequestQueueManager queue,
+ String baseUrl,
+ EthHttpClient executor,
+ Converter converter,
+ int retry) {
+ super(queue, "stats", baseUrl, executor, converter, retry);
}
@NotNull
@Override
public Wei supply() throws EtherScanException {
- final StringResponseTO response = getRequest(ACT_SUPPLY_PARAM, StringResponseTO.class);
+ final StringResponseTO response = getResponse(ACT_SUPPLY_PARAM, StringResponseTO.class);
if (response.getStatus() != 1)
throw new EtherScanResponseException(response);
@@ -49,7 +50,7 @@ public Wei supply() throws EtherScanException {
@Override
public @NotNull EthSupply supplyTotal() throws EtherScanException {
- final EthSupplyResponseTO response = getRequest(ACT_SUPPLY2_PARAM, EthSupplyResponseTO.class);
+ final EthSupplyResponseTO response = getResponse(ACT_SUPPLY2_PARAM, EthSupplyResponseTO.class);
if (response.getStatus() != 1)
throw new EtherScanResponseException(response);
@@ -62,7 +63,7 @@ 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);
+ final StringResponseTO response = getResponse(urlParams, StringResponseTO.class);
if (response.getStatus() != 1)
throw new EtherScanResponseException(response);
@@ -72,7 +73,7 @@ public Wei supply(@NotNull String contract) throws EtherScanException {
@NotNull
@Override
public Price priceLast() throws EtherScanException {
- final PriceResponseTO response = getRequest(ACT_LASTPRICE_PARAM, PriceResponseTO.class);
+ final PriceResponseTO response = getResponse(ACT_LASTPRICE_PARAM, PriceResponseTO.class);
if (response.getStatus() != 1)
throw new EtherScanResponseException(response);
diff --git a/src/main/java/io/goodforgod/api/etherscan/TransactionAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/TransactionAPIProvider.java
index da26b51..61f2484 100644
--- a/src/main/java/io/goodforgod/api/etherscan/TransactionAPIProvider.java
+++ b/src/main/java/io/goodforgod/api/etherscan/TransactionAPIProvider.java
@@ -17,18 +17,19 @@
* @see TransactionAPI
* @since 28.10.2018
*/
-final class TransactionAPIProvider extends BasicProvider implements TransactionAPI {
+public 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(RequestQueueManager queue,
- String baseUrl,
- EthHttpClient executor,
- Converter converter) {
- super(queue, "transaction", baseUrl, executor, converter);
+ public TransactionAPIProvider(RequestQueueManager queue,
+ String baseUrl,
+ EthHttpClient executor,
+ Converter converter,
+ int retryCount) {
+ super(queue, "transaction", baseUrl, executor, converter, retryCount);
}
@NotNull
@@ -37,7 +38,7 @@ public Optional statusExec(@NotNull String txhash) throws EtherScanExcep
BasicUtils.validateTxHash(txhash);
final String urlParams = ACT_EXEC_STATUS_PARAM + TXHASH_PARAM + txhash;
- final StatusResponseTO response = getRequest(urlParams, StatusResponseTO.class);
+ final StatusResponseTO response = getResponse(urlParams, StatusResponseTO.class);
BasicUtils.validateTxResponse(response);
return Optional.ofNullable(response.getResult());
@@ -49,7 +50,7 @@ public Optional statusReceipt(@NotNull String txhash) throws EtherScanE
BasicUtils.validateTxHash(txhash);
final String urlParams = ACT_RECEIPT_STATUS_PARAM + TXHASH_PARAM + txhash;
- final ReceiptStatusResponseTO response = getRequest(urlParams, ReceiptStatusResponseTO.class);
+ final ReceiptStatusResponseTO response = getResponse(urlParams, ReceiptStatusResponseTO.class);
BasicUtils.validateTxResponse(response);
return (response.getResult() == null || BasicUtils.isEmpty(response.getResult().getStatus()))
diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanConnectionTimeoutException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanConnectionTimeoutException.java
new file mode 100644
index 0000000..1b2ff22
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanConnectionTimeoutException.java
@@ -0,0 +1,12 @@
+package io.goodforgod.api.etherscan.error;
+
+/**
+ * @author GoodforGod
+ * @since 12.11.2018
+ */
+public class EtherScanConnectionTimeoutException extends EtherScanConnectionException {
+
+ public EtherScanConnectionTimeoutException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanTimeoutException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanTimeoutException.java
deleted file mode 100644
index 7734139..0000000
--- a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanTimeoutException.java
+++ /dev/null
@@ -1,12 +0,0 @@
-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
index bd01f83..28142b8 100644
--- a/src/main/java/io/goodforgod/api/etherscan/http/EthHttpClient.java
+++ b/src/main/java/io/goodforgod/api/etherscan/http/EthHttpClient.java
@@ -17,7 +17,7 @@ public interface EthHttpClient {
* @param uri as string
* @return result as string
*/
- byte[] get(@NotNull URI uri);
+ EthResponse get(@NotNull URI uri);
/**
* Performs a Http POST request
@@ -26,5 +26,5 @@ public interface EthHttpClient {
* @param body to post
* @return result as string
*/
- byte[] post(@NotNull URI uri, byte[] body);
+ EthResponse post(@NotNull URI uri, byte[] body);
}
diff --git a/src/main/java/io/goodforgod/api/etherscan/http/EthResponse.java b/src/main/java/io/goodforgod/api/etherscan/http/EthResponse.java
new file mode 100644
index 0000000..20b7d81
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/http/EthResponse.java
@@ -0,0 +1,30 @@
+package io.goodforgod.api.etherscan.http;
+
+import java.util.List;
+import java.util.Map;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Anton Kurako (GoodforGod)
+ *
+ * @since 07.09.2025
+ */
+public interface EthResponse {
+
+ int statusCode();
+
+ @Nullable
+ byte[] body();
+
+ @NotNull
+ Map> headers();
+
+ static EthResponse of(int statusCode, @Nullable byte[] body) {
+ return new SimpleEthResponse(statusCode, body, null);
+ }
+
+ static EthResponse of(int statusCode, @Nullable byte[] body, @Nullable Map> headers) {
+ return new SimpleEthResponse(statusCode, body, headers);
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/http/SimpleEthResponse.java b/src/main/java/io/goodforgod/api/etherscan/http/SimpleEthResponse.java
new file mode 100644
index 0000000..2e9ebf4
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/http/SimpleEthResponse.java
@@ -0,0 +1,64 @@
+package io.goodforgod.api.etherscan.http;
+
+import java.util.*;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Anton Kurako (GoodforGod)
+ *
+ * @since 07.09.2025
+ */
+class SimpleEthResponse implements EthResponse {
+
+ private final int statusCode;
+ @Nullable
+ private final byte[] body;
+ private final Map> headers;
+
+ SimpleEthResponse(int statusCode, @Nullable byte[] body, @Nullable Map> headers) {
+ this.statusCode = statusCode;
+ this.body = body;
+ this.headers = (headers == null)
+ ? Collections.emptyMap()
+ : headers;
+ }
+
+ @Override
+ public int statusCode() {
+ return statusCode;
+ }
+
+ @Nullable
+ @Override
+ public byte[] body() {
+ return body;
+ }
+
+ @Override
+ public @NotNull Map> headers() {
+ return headers;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ SimpleEthResponse that = (SimpleEthResponse) o;
+ return statusCode == that.statusCode && Arrays.equals(body, that.body) && Objects.equals(headers, that.headers);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hash(statusCode, headers);
+ result = 31 * result + Arrays.hashCode(body);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "{\"statusCode\": " + statusCode + ", \"headers\": " + headers + ", \"body\": \"" + Arrays.toString(body) + "\"}";
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/http/impl/JdkEthHttpClient.java b/src/main/java/io/goodforgod/api/etherscan/http/impl/JdkEthHttpClient.java
new file mode 100644
index 0000000..01580ce
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/http/impl/JdkEthHttpClient.java
@@ -0,0 +1,186 @@
+package io.goodforgod.api.etherscan.http.impl;
+
+import io.goodforgod.api.etherscan.error.EtherScanConnectionException;
+import io.goodforgod.api.etherscan.error.EtherScanConnectionTimeoutException;
+import io.goodforgod.api.etherscan.http.EthHttpClient;
+import io.goodforgod.api.etherscan.http.EthResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpConnectTimeoutException;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.time.Duration;
+import java.util.*;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.InflaterInputStream;
+import org.jetbrains.annotations.ApiStatus.Internal;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Anton Kurako (GoodforGod)
+ *
+ * @since 07.09.2025
+ */
+@Internal
+public class JdkEthHttpClient 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.ofMinutes(2);
+
+ 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 HttpClient httpClient;
+ private final Duration requestTimeout;
+ private final Map headers;
+ private final UrlEthHttpClient urlEthHttpClient;
+
+ public JdkEthHttpClient() {
+ this(HttpClient.newBuilder()
+ .connectTimeout(DEFAULT_CONNECT_TIMEOUT)
+ .followRedirects(HttpClient.Redirect.NORMAL)
+ .version(HttpClient.Version.HTTP_2)
+ .build(),
+ DEFAULT_READ_TIMEOUT,
+ DEFAULT_HEADERS);
+ }
+
+ public JdkEthHttpClient(HttpClient httpClient) {
+ this(httpClient, DEFAULT_READ_TIMEOUT, DEFAULT_HEADERS);
+ }
+
+ public JdkEthHttpClient(HttpClient httpClient, Duration requestTimeout) {
+ this(httpClient, requestTimeout, DEFAULT_HEADERS);
+ }
+
+ public JdkEthHttpClient(HttpClient httpClient, Duration requestTimeout, Map headers) {
+ this.httpClient = httpClient;
+ this.requestTimeout = requestTimeout;
+ this.headers = headers;
+ this.urlEthHttpClient = new UrlEthHttpClient(DEFAULT_CONNECT_TIMEOUT, requestTimeout, headers);
+ }
+
+ @Override
+ public EthResponse get(@NotNull URI uri) {
+ HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
+ .GET()
+ .uri(uri)
+ .timeout(requestTimeout);
+
+ headers.forEach(requestBuilder::header);
+
+ try {
+ HttpResponse response = httpClient.send(requestBuilder.build(),
+ HttpResponse.BodyHandlers.ofInputStream());
+ byte[] bodyAsBytes = getDeflatedBytes(response);
+ return new EthResponse() {
+
+ @Override
+ public int statusCode() {
+ return response.statusCode();
+ }
+
+ @Override
+ public byte[] body() {
+ return bodyAsBytes;
+ }
+
+ @Override
+ public @NotNull Map> headers() {
+ return response.headers().map();
+ }
+ };
+ } catch (HttpConnectTimeoutException e) {
+ throw new EtherScanConnectionTimeoutException(
+ "Connection Timeout: Could not establish connection to Etherscan server for "
+ + httpClient.connectTimeout().orElse(DEFAULT_CONNECT_TIMEOUT) + " millis",
+ e);
+ } catch (IOException e) {
+ throw new EtherScanConnectionException("Etherscan HTTP server network error occurred: " + e.getMessage(), e);
+ } catch (InterruptedException e) {
+ throw new EtherScanConnectionException("Etherscan HTTP server interrupt exception occurred: " + e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public EthResponse post(@NotNull URI uri, byte[] body) {
+ return urlEthHttpClient.post(uri, body);
+ }
+
+ // content-length somehow is not working properly and can't force user to override
+ // "jdk.httpclient.allowRestrictedHeaders" prop, so will force use UrlEthHttpClient
+ private EthResponse postJdk(@NotNull URI uri, byte[] body) {
+ HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
+ .POST(HttpRequest.BodyPublishers.ofByteArray(body))
+ .uri(uri)
+ .timeout(requestTimeout);
+
+ headers.forEach(requestBuilder::header);
+ requestBuilder.header("Content-Type", "application/json; charset=UTF-8");
+
+ try {
+ HttpResponse response = httpClient.send(requestBuilder.build(),
+ HttpResponse.BodyHandlers.ofInputStream());
+ byte[] bodyAsBytes = getDeflatedBytes(response);
+ return new EthResponse() {
+
+ @Override
+ public int statusCode() {
+ return response.statusCode();
+ }
+
+ @Override
+ public byte[] body() {
+ return bodyAsBytes;
+ }
+
+ @Override
+ public @NotNull Map> headers() {
+ return response.headers().map();
+ }
+ };
+ } catch (HttpConnectTimeoutException e) {
+ throw new EtherScanConnectionTimeoutException(
+ "Connection Timeout: Could not establish connection to Etherscan server for "
+ + httpClient.connectTimeout().orElse(DEFAULT_CONNECT_TIMEOUT) + " millis",
+ e);
+ } catch (IOException e) {
+ throw new EtherScanConnectionException("Etherscan HTTP server network error occurred: " + e.getMessage(), e);
+ } catch (InterruptedException e) {
+ throw new EtherScanConnectionException("Etherscan HTTP server interrupt exception occurred: " + e.getMessage(), e);
+ }
+ }
+
+ private static byte[] getDeflatedBytes(HttpResponse response) {
+ try {
+ Optional encoding = response.headers().firstValue("content-encoding");
+ if (encoding.isEmpty()) {
+ try (var is = response.body()) {
+ return is.readAllBytes();
+ }
+ }
+
+ switch (encoding.get().strip().toLowerCase(Locale.ROOT)) {
+ case "gzip":
+ return new GZIPInputStream(response.body()).readAllBytes();
+ case "deflate":
+ return new InflaterInputStream(response.body()).readAllBytes();
+ default:
+ try (var is = response.body()) {
+ return is.readAllBytes();
+ }
+ }
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+}
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
index b298743..58f4658 100644
--- a/src/main/java/io/goodforgod/api/etherscan/http/impl/UrlEthHttpClient.java
+++ b/src/main/java/io/goodforgod/api/etherscan/http/impl/UrlEthHttpClient.java
@@ -3,20 +3,23 @@
import static java.net.HttpURLConnection.*;
import io.goodforgod.api.etherscan.error.EtherScanConnectionException;
-import io.goodforgod.api.etherscan.error.EtherScanTimeoutException;
+import io.goodforgod.api.etherscan.error.EtherScanConnectionTimeoutException;
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 io.goodforgod.api.etherscan.http.EthResponse;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.*;
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.ApiStatus.Internal;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
/**
* Http client implementation
@@ -25,12 +28,13 @@
* @see EthHttpClient
* @since 28.10.2018
*/
-public final class UrlEthHttpClient implements EthHttpClient {
+@Internal
+public 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;
+ private static final Duration DEFAULT_READ_TIMEOUT = Duration.ofMinutes(2);
static {
DEFAULT_HEADERS.put("Accept-Language", "en");
@@ -39,6 +43,8 @@ public final class UrlEthHttpClient implements EthHttpClient {
DEFAULT_HEADERS.put("Accept-Charset", "UTF-8");
}
+ @Nullable
+ private final Proxy proxy;
private final Map headers;
private final int connectTimeout;
private final int readTimeout;
@@ -48,11 +54,19 @@ public UrlEthHttpClient() {
}
public UrlEthHttpClient(Duration connectTimeout) {
- this(connectTimeout, DEFAULT_READ_TIMEOUT);
+ this(connectTimeout, DEFAULT_READ_TIMEOUT, DEFAULT_HEADERS, null);
}
public UrlEthHttpClient(Duration connectTimeout, Duration readTimeout) {
- this(connectTimeout, readTimeout, DEFAULT_HEADERS);
+ this(connectTimeout, readTimeout, DEFAULT_HEADERS, null);
+ }
+
+ public UrlEthHttpClient(Duration connectTimeout, Duration readTimeout, Proxy proxy) {
+ this(connectTimeout, readTimeout, DEFAULT_HEADERS, null);
+ }
+
+ public UrlEthHttpClient(Duration connectTimeout, Duration readTimeout, Map headers) {
+ this(connectTimeout, readTimeout, headers, null);
}
/**
@@ -62,15 +76,19 @@ public UrlEthHttpClient(Duration connectTimeout, Duration readTimeout) {
*/
public UrlEthHttpClient(Duration connectTimeout,
Duration readTimeout,
- Map headers) {
+ Map headers,
+ @Nullable Proxy proxy) {
this.connectTimeout = Math.toIntExact(connectTimeout.toMillis());
this.readTimeout = Math.toIntExact(readTimeout.toMillis());
this.headers = Collections.unmodifiableMap(headers);
+ this.proxy = proxy;
}
private HttpURLConnection buildConnection(URI uri, String method) throws IOException {
final URL url = uri.toURL();
- final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ final HttpURLConnection connection = (proxy == null)
+ ? (HttpURLConnection) url.openConnection()
+ : (HttpURLConnection) url.openConnection(proxy);
connection.setRequestMethod(method);
connection.setConnectTimeout(connectTimeout);
connection.setReadTimeout(readTimeout);
@@ -79,7 +97,7 @@ private HttpURLConnection buildConnection(URI uri, String method) throws IOExcep
}
@Override
- public byte[] get(@NotNull URI uri) {
+ public EthResponse get(@NotNull URI uri) {
try {
final HttpURLConnection connection = buildConnection(uri, "GET");
final int status = connection.getResponseCode();
@@ -92,17 +110,19 @@ public byte[] get(@NotNull URI uri) {
}
final byte[] data = readData(connection);
+ EthResponse ethResponse = EthResponse.of(connection.getResponseCode(), data, connection.getHeaderFields());
connection.disconnect();
- return data;
+ return ethResponse;
} catch (SocketTimeoutException e) {
- throw new EtherScanTimeoutException("Timeout: Could not establish connection for " + connectTimeout + " millis", e);
+ throw new EtherScanConnectionTimeoutException(
+ "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) {
+ public EthResponse post(@NotNull URI uri, byte[] body) {
try {
final HttpURLConnection connection = buildConnection(uri, "POST");
final int contentLength = body.length;
@@ -126,10 +146,12 @@ public byte[] post(@NotNull URI uri, byte[] body) {
}
final byte[] data = readData(connection);
+ EthResponse ethResponse = EthResponse.of(connection.getResponseCode(), data, connection.getHeaderFields());
connection.disconnect();
- return data;
+ return ethResponse;
} catch (SocketTimeoutException e) {
- throw new EtherScanTimeoutException("Timeout: Could not establish connection for " + connectTimeout + " millis", e);
+ throw new EtherScanConnectionTimeoutException(
+ "Timeout: Could not establish connection for " + connectTimeout + " millis", e);
} catch (Exception e) {
throw new EtherScanConnectionException(e.getMessage(), e);
}
diff --git a/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java b/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java
index 0f36b23..92875d0 100644
--- a/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java
+++ b/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java
@@ -17,7 +17,7 @@ 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));
+ return new SemaphoreRequestQueueManager(1, Duration.ofMillis(5045L));
}
/**
@@ -25,19 +25,19 @@ static RequestQueueManager anonymous() {
* Free API KEY
*/
static RequestQueueManager planFree() {
- return new SemaphoreRequestQueueManager(5, Duration.ofMillis(1015L));
+ return new SemaphoreRequestQueueManager(5, Duration.ofMillis(1045L));
}
static RequestQueueManager planStandard() {
- return new SemaphoreRequestQueueManager(10, Duration.ofMillis(1015L));
+ return new SemaphoreRequestQueueManager(10, Duration.ofMillis(1045L));
}
static RequestQueueManager planAdvanced() {
- return new SemaphoreRequestQueueManager(20, Duration.ofMillis(1015L));
+ return new SemaphoreRequestQueueManager(20, Duration.ofMillis(1045L));
}
static RequestQueueManager planProfessional() {
- return new SemaphoreRequestQueueManager(30, Duration.ofMillis(1015L));
+ return new SemaphoreRequestQueueManager(30, Duration.ofMillis(1045L));
}
static RequestQueueManager unlimited() {
diff --git a/src/main/java/io/goodforgod/api/etherscan/manager/impl/FakeRequestQueueManager.java b/src/main/java/io/goodforgod/api/etherscan/manager/impl/FakeRequestQueueManager.java
index 626b4c1..ed81b94 100644
--- a/src/main/java/io/goodforgod/api/etherscan/manager/impl/FakeRequestQueueManager.java
+++ b/src/main/java/io/goodforgod/api/etherscan/manager/impl/FakeRequestQueueManager.java
@@ -8,7 +8,7 @@
* @author GoodforGod
* @since 03.11.2018
*/
-public final class FakeRequestQueueManager implements RequestQueueManager {
+public 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
index 2a3483c..44c6bd5 100644
--- a/src/main/java/io/goodforgod/api/etherscan/manager/impl/SemaphoreRequestQueueManager.java
+++ b/src/main/java/io/goodforgod/api/etherscan/manager/impl/SemaphoreRequestQueueManager.java
@@ -14,7 +14,7 @@
* @author GoodforGod
* @since 30.10.2018
*/
-public final class SemaphoreRequestQueueManager implements RequestQueueManager, AutoCloseable {
+public class SemaphoreRequestQueueManager implements RequestQueueManager, AutoCloseable {
private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
private final Semaphore semaphore;
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Abi.java b/src/main/java/io/goodforgod/api/etherscan/model/Abi.java
index 3536bf9..21b6601 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/Abi.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/Abi.java
@@ -55,7 +55,7 @@ public int hashCode() {
@Override
public String toString() {
return "Abi{" +
- "contractAbi='" + contractAbi + '\'' +
+ "contractAbi=" + contractAbi +
", isVerified=" + isVerified +
'}';
}
@@ -64,7 +64,7 @@ public static AbiBuilder builder() {
return new AbiBuilder();
}
- public static final class AbiBuilder {
+ public static class AbiBuilder {
private String contractAbi;
private boolean 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
index 079d4b6..1d2f743 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/Balance.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/Balance.java
@@ -45,7 +45,7 @@ public int hashCode() {
@Override
public String toString() {
return "Balance{" +
- "address='" + address + '\'' +
+ "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
index 8de679a..e0fc376 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/BaseTx.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/BaseTx.java
@@ -12,7 +12,7 @@
* @author GoodforGod
* @since 28.10.2018
*/
-abstract class BaseTx implements Comparable {
+public abstract class BaseTx implements Comparable {
long blockNumber;
String timeStamp;
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Block.java b/src/main/java/io/goodforgod/api/etherscan/model/Block.java
index 0550000..da1184b 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/Block.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/Block.java
@@ -58,7 +58,7 @@ public String toString() {
return "Block{" +
"blockNumber=" + blockNumber +
", blockReward=" + blockReward +
- ", timeStamp='" + timeStamp + '\'' +
+ ", timeStamp=" + timeStamp +
'}';
}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/BlockTx.java b/src/main/java/io/goodforgod/api/etherscan/model/BlockTx.java
index f3c4d67..f77e5d4 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/BlockTx.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/BlockTx.java
@@ -8,7 +8,7 @@
* @author Anton Kurako (GoodforGod)
* @since 15.05.2023
*/
-abstract class BlockTx extends BaseTx {
+public abstract class BlockTx extends BaseTx {
long nonce;
String blockHash;
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java b/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java
index 9b110d9..058e13b 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java
@@ -19,7 +19,7 @@ public static class Uncle {
private BigInteger blockreward;
private int unclePosition;
- private Uncle() {}
+ protected Uncle() {}
//
public String getMiner() {
@@ -54,7 +54,7 @@ public int hashCode() {
@Override
public String toString() {
return "Uncle{" +
- "miner='" + miner + '\'' +
+ "miner=" + miner +
", blockreward=" + blockreward +
", unclePosition=" + unclePosition +
'}';
@@ -64,7 +64,7 @@ public static UncleBuilder builder() {
return new UncleBuilder();
}
- public static final class UncleBuilder {
+ public static class UncleBuilder {
private String miner;
private BigInteger blockreward;
@@ -128,9 +128,9 @@ public String getUncleInclusionReward() {
@Override
public String toString() {
return "UncleBlock{" +
- "blockMiner='" + blockMiner + '\'' +
+ "blockMiner=" + blockMiner +
", uncles=" + uncles +
- ", uncleInclusionReward='" + uncleInclusionReward + '\'' +
+ ", uncleInclusionReward=" + uncleInclusionReward +
'}';
}
@@ -138,7 +138,7 @@ public static BlockUncleBuilder builder() {
return new BlockUncleBuilder();
}
- public static final class BlockUncleBuilder extends Block.BlockBuilder {
+ public static class BlockUncleBuilder extends Block.BlockBuilder {
private long blockNumber;
private BigInteger blockReward;
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/ContractCreation.java b/src/main/java/io/goodforgod/api/etherscan/model/ContractCreation.java
new file mode 100644
index 0000000..41e62c5
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/ContractCreation.java
@@ -0,0 +1,86 @@
+package io.goodforgod.api.etherscan.model;
+
+import java.util.Objects;
+
+public class ContractCreation {
+
+ private String contractAddress;
+ private String contractCreator;
+ private String txHash;
+
+ protected ContractCreation() {}
+
+ public String getContractAddress() {
+ return contractAddress;
+ }
+
+ public String getContractCreator() {
+ return contractCreator;
+ }
+
+ public String getTxHash() {
+ return txHash;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ ContractCreation that = (ContractCreation) o;
+ return Objects.equals(contractAddress, that.contractAddress)
+ && Objects.equals(contractCreator, that.contractCreator)
+ && Objects.equals(txHash, that.txHash);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(contractAddress, contractCreator, txHash);
+ }
+
+ @Override
+ public String toString() {
+ return "ContractCreation{" +
+ "contractAddress=" + contractAddress +
+ ", contractCreator=" + contractCreator +
+ ", txHash=" + txHash +
+ '}';
+ }
+
+ public static ContractCreationBuilder builder() {
+ return new ContractCreationBuilder();
+ }
+
+ public static class ContractCreationBuilder {
+
+ private String contractAddress;
+ private String contractCreator;
+ private String txHash;
+
+ private ContractCreationBuilder() {}
+
+ public ContractCreationBuilder withContractAddress(String contractAddress) {
+ this.contractAddress = contractAddress;
+ return this;
+ }
+
+ public ContractCreationBuilder withContractCreator(String contractCreator) {
+ this.contractCreator = contractCreator;
+ return this;
+ }
+
+ public ContractCreationBuilder withTxHash(String txHash) {
+ this.txHash = txHash;
+ return this;
+ }
+
+ public ContractCreation build() {
+ ContractCreation contractCreation = new ContractCreation();
+ contractCreation.contractAddress = contractAddress;
+ contractCreation.contractCreator = contractCreator;
+ contractCreation.txHash = txHash;
+ return contractCreation;
+ }
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/EthSupply.java b/src/main/java/io/goodforgod/api/etherscan/model/EthSupply.java
index 344e754..4dbbce7 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/EthSupply.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/EthSupply.java
@@ -56,10 +56,10 @@ public int hashCode() {
@Override
public String toString() {
return "EthSupply{" +
- "EthSupply='" + EthSupply + '\'' +
- ", Eth2Staking='" + Eth2Staking + '\'' +
- ", BurntFees='" + BurntFees + '\'' +
- ", WithdrawnTotal='" + WithdrawnTotal + '\'' +
+ "EthSupply=" + EthSupply +
+ ", Eth2Staking=" + Eth2Staking +
+ ", BurntFees=" + BurntFees +
+ ", WithdrawnTotal=" + WithdrawnTotal +
'}';
}
@@ -67,7 +67,7 @@ public static EthSupplyBuilder builder() {
return new EthSupplyBuilder();
}
- public static final class EthSupplyBuilder {
+ public static class EthSupplyBuilder {
private Wei ethSupply;
private Wei eth2Staking;
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java b/src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java
index 6fe1231..e7726f9 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java
@@ -1,7 +1,6 @@
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;
@@ -14,9 +13,9 @@
public class GasOracle {
private Long LastBlock;
- private BigInteger SafeGasPrice;
- private BigInteger ProposeGasPrice;
- private BigInteger FastGasPrice;
+ private BigDecimal SafeGasPrice;
+ private BigDecimal ProposeGasPrice;
+ private BigDecimal FastGasPrice;
private BigDecimal suggestBaseFee;
private String gasUsedRatio;
@@ -83,7 +82,7 @@ public static GasOracleBuilder builder() {
return new GasOracleBuilder();
}
- public static final class GasOracleBuilder {
+ public static class GasOracleBuilder {
private Long lastBlock;
private Wei safeGasPrice;
@@ -129,13 +128,13 @@ public GasOracle build() {
gasOracle.LastBlock = this.lastBlock;
gasOracle.suggestBaseFee = this.suggestBaseFee;
if (this.proposeGasPrice != null) {
- gasOracle.ProposeGasPrice = this.proposeGasPrice.asGwei().toBigInteger();
+ gasOracle.ProposeGasPrice = this.proposeGasPrice.asGwei();
}
if (this.safeGasPrice != null) {
- gasOracle.SafeGasPrice = this.safeGasPrice.asGwei().toBigInteger();
+ gasOracle.SafeGasPrice = this.safeGasPrice.asGwei();
}
if (this.fastGasPrice != null) {
- gasOracle.FastGasPrice = this.fastGasPrice.asGwei().toBigInteger();
+ gasOracle.FastGasPrice = this.fastGasPrice.asGwei();
}
if (this.gasUsedRatio != null) {
gasOracle.gasUsedRatio = this.gasUsedRatio.stream()
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Log.java b/src/main/java/io/goodforgod/api/etherscan/model/Log.java
index da6c295..d29db31 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/Log.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/Log.java
@@ -138,16 +138,16 @@ public int hashCode() {
@Override
public String toString() {
return "Log{" +
- "blockNumber='" + blockNumber + '\'' +
- ", address='" + address + '\'' +
- ", transactionHash='" + transactionHash + '\'' +
- ", transactionIndex='" + transactionIndex + '\'' +
- ", timeStamp='" + timeStamp + '\'' +
- ", data='" + data + '\'' +
- ", gasPrice='" + gasPrice + '\'' +
- ", gasUsed='" + gasUsed + '\'' +
+ "blockNumber=" + blockNumber +
+ ", address=" + address +
+ ", transactionHash=" + transactionHash +
+ ", transactionIndex=" + transactionIndex +
+ ", timeStamp=" + timeStamp +
+ ", data=" + data +
+ ", gasPrice=" + gasPrice +
+ ", gasUsed=" + gasUsed +
", topics=" + topics +
- ", logIndex='" + logIndex + '\'' +
+ ", logIndex=" + logIndex +
'}';
}
@@ -155,7 +155,7 @@ public static LogBuilder builder() {
return new LogBuilder();
}
- public static final class LogBuilder {
+ public static class LogBuilder {
private Long blockNumber;
private String address;
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Price.java b/src/main/java/io/goodforgod/api/etherscan/model/Price.java
index 565dbed..0baa38a 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/Price.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/Price.java
@@ -67,8 +67,8 @@ public String toString() {
return "Price{" +
"ethusd=" + ethusd +
", ethbtc=" + ethbtc +
- ", ethusd_timestamp='" + ethusd_timestamp + '\'' +
- ", ethbtc_timestamp='" + ethbtc_timestamp + '\'' +
+ ", ethusd_timestamp=" + ethusd_timestamp +
+ ", ethbtc_timestamp=" + ethbtc_timestamp +
'}';
}
@@ -76,7 +76,7 @@ public static PriceBuilder builder() {
return new PriceBuilder();
}
- public static final class PriceBuilder {
+ public static class PriceBuilder {
private BigDecimal ethusd;
private BigDecimal ethbtc;
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Status.java b/src/main/java/io/goodforgod/api/etherscan/model/Status.java
index 052c187..f651b1f 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/Status.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/Status.java
@@ -45,7 +45,7 @@ public int hashCode() {
public String toString() {
return "Status{" +
"isError=" + isError +
- ", errDescription='" + errDescription + '\'' +
+ ", errDescription=" + errDescription +
'}';
}
@@ -53,7 +53,7 @@ public static StatusBuilder builder() {
return new StatusBuilder();
}
- public static final class StatusBuilder {
+ public static class StatusBuilder {
private int isError;
private String errDescription;
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TokenBalance.java b/src/main/java/io/goodforgod/api/etherscan/model/TokenBalance.java
index bb40ee2..c257654 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/TokenBalance.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/TokenBalance.java
@@ -39,7 +39,7 @@ public int hashCode() {
@Override
public String toString() {
return "TokenBalance{" +
- "tokenContract='" + tokenContract + '\'' +
+ "tokenContract=" + tokenContract +
'}';
}
}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Tx.java b/src/main/java/io/goodforgod/api/etherscan/model/Tx.java
index 7ef0e22..8843bd6 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/Tx.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/Tx.java
@@ -35,21 +35,21 @@ public String getTxReceiptStatus() {
public String toString() {
return "Tx{" +
"value=" + value +
- ", isError='" + isError + '\'' +
- ", txreceipt_status='" + txreceipt_status + '\'' +
+ ", isError=" + isError +
+ ", txreceipt_status=" + txreceipt_status +
", nonce=" + nonce +
- ", blockHash='" + blockHash + '\'' +
+ ", blockHash=" + blockHash +
", transactionIndex=" + transactionIndex +
", confirmations=" + confirmations +
", gasPrice=" + gasPrice +
", cumulativeGasUsed=" + cumulativeGasUsed +
", blockNumber=" + blockNumber +
- ", timeStamp='" + timeStamp + '\'' +
- ", hash='" + hash + '\'' +
- ", from='" + from + '\'' +
- ", to='" + to + '\'' +
- ", contractAddress='" + contractAddress + '\'' +
- ", input='" + input + '\'' +
+ ", timeStamp=" + timeStamp +
+ ", hash=" + hash +
+ ", from=" + from +
+ ", to=" + to +
+ ", contractAddress=" + contractAddress +
+ ", input=" + input +
", gas=" + gas +
", gasUsed=" + gasUsed +
'}';
@@ -59,7 +59,7 @@ public static TxBuilder builder() {
return new TxBuilder();
}
- public static final class TxBuilder {
+ public static class TxBuilder {
private long blockNumber;
private LocalDateTime timeStamp;
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java b/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java
index 16d4457..ed8754d 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java
@@ -56,23 +56,23 @@ public int hashCode() {
@Override
public String toString() {
return "TxErc1155{" +
- "tokenID='" + tokenID + '\'' +
- ", tokenName='" + tokenName + '\'' +
- ", tokenSymbol='" + tokenSymbol + '\'' +
- ", tokenValue='" + tokenValue + '\'' +
+ "tokenID=" + tokenID +
+ ", tokenName=" + tokenName +
+ ", tokenSymbol=" + tokenSymbol +
+ ", tokenValue=" + tokenValue +
", nonce=" + nonce +
- ", blockHash='" + blockHash + '\'' +
+ ", blockHash=" + blockHash +
", transactionIndex=" + transactionIndex +
", confirmations=" + confirmations +
", gasPrice=" + gasPrice +
", cumulativeGasUsed=" + cumulativeGasUsed +
", blockNumber=" + blockNumber +
- ", timeStamp='" + timeStamp + '\'' +
- ", hash='" + hash + '\'' +
- ", from='" + from + '\'' +
- ", to='" + to + '\'' +
- ", contractAddress='" + contractAddress + '\'' +
- ", input='" + input + '\'' +
+ ", timeStamp=" + timeStamp +
+ ", hash=" + hash +
+ ", from=" + from +
+ ", to=" + to +
+ ", contractAddress=" + contractAddress +
+ ", input=" + input +
", gas=" + gas +
", gasUsed=" + gasUsed +
'}';
@@ -82,7 +82,7 @@ public static TxErc1155Builder builder() {
return new TxErc1155Builder();
}
- public static final class TxErc1155Builder {
+ public static class TxErc1155Builder {
private long blockNumber;
private LocalDateTime timeStamp;
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java b/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java
index 3dc22fd..57d70cb 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java
@@ -58,22 +58,22 @@ public int hashCode() {
public String toString() {
return "TxErc20{" +
"value=" + value +
- ", tokenName='" + tokenName + '\'' +
- ", tokenSymbol='" + tokenSymbol + '\'' +
- ", tokenDecimal='" + tokenDecimal + '\'' +
+ ", tokenName=" + tokenName +
+ ", tokenSymbol=" + tokenSymbol +
+ ", tokenDecimal=" + tokenDecimal +
", nonce=" + nonce +
- ", blockHash='" + blockHash + '\'' +
+ ", blockHash=" + blockHash +
", transactionIndex=" + transactionIndex +
", confirmations=" + confirmations +
", gasPrice=" + gasPrice +
", cumulativeGasUsed=" + cumulativeGasUsed +
", blockNumber=" + blockNumber +
- ", timeStamp='" + timeStamp + '\'' +
- ", hash='" + hash + '\'' +
- ", from='" + from + '\'' +
- ", to='" + to + '\'' +
- ", contractAddress='" + contractAddress + '\'' +
- ", input='" + input + '\'' +
+ ", timeStamp=" + timeStamp +
+ ", hash=" + hash +
+ ", from=" + from +
+ ", to=" + to +
+ ", contractAddress=" + contractAddress +
+ ", input=" + input +
", gas=" + gas +
", gasUsed=" + gasUsed +
'}';
@@ -83,7 +83,7 @@ public static TxERC20Builder builder() {
return new TxERC20Builder();
}
- public static final class TxERC20Builder {
+ public static class TxERC20Builder {
private long blockNumber;
private LocalDateTime timeStamp;
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java b/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java
index 2180019..64df779 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java
@@ -56,23 +56,23 @@ public int hashCode() {
@Override
public String toString() {
return "TxErc721{" +
- "tokenID='" + tokenID + '\'' +
- ", tokenName='" + tokenName + '\'' +
- ", tokenSymbol='" + tokenSymbol + '\'' +
- ", tokenDecimal='" + tokenDecimal + '\'' +
+ "tokenID=" + tokenID +
+ ", tokenName=" + tokenName +
+ ", tokenSymbol=" + tokenSymbol +
+ ", tokenDecimal=" + tokenDecimal +
", nonce=" + nonce +
- ", blockHash='" + blockHash + '\'' +
+ ", blockHash=" + blockHash +
", transactionIndex=" + transactionIndex +
", confirmations=" + confirmations +
", gasPrice=" + gasPrice +
", cumulativeGasUsed=" + cumulativeGasUsed +
", blockNumber=" + blockNumber +
- ", timeStamp='" + timeStamp + '\'' +
- ", hash='" + hash + '\'' +
- ", from='" + from + '\'' +
- ", to='" + to + '\'' +
- ", contractAddress='" + contractAddress + '\'' +
- ", input='" + input + '\'' +
+ ", timeStamp=" + timeStamp +
+ ", hash=" + hash +
+ ", from=" + from +
+ ", to=" + to +
+ ", contractAddress=" + contractAddress +
+ ", input=" + input +
", gas=" + gas +
", gasUsed=" + gasUsed +
'}';
@@ -82,7 +82,7 @@ public static TxERC721Builder builder() {
return new TxERC721Builder();
}
- public static final class TxERC721Builder {
+ public static class TxERC721Builder {
private long blockNumber;
private LocalDateTime timeStamp;
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java b/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java
index a61cf83..91c2b27 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java
@@ -69,17 +69,17 @@ public int hashCode() {
public String toString() {
return "TxInternal{" +
"value=" + value +
- ", type='" + type + '\'' +
- ", traceId='" + traceId + '\'' +
+ ", type=" + type +
+ ", traceId=" + traceId +
", isError=" + isError +
- ", errCode='" + errCode + '\'' +
+ ", errCode=" + errCode +
", blockNumber=" + blockNumber +
- ", timeStamp='" + timeStamp + '\'' +
- ", hash='" + hash + '\'' +
- ", from='" + from + '\'' +
- ", to='" + to + '\'' +
- ", contractAddress='" + contractAddress + '\'' +
- ", input='" + input + '\'' +
+ ", timeStamp=" + timeStamp +
+ ", hash=" + hash +
+ ", from=" + from +
+ ", to=" + to +
+ ", contractAddress=" + contractAddress +
+ ", input=" + input +
", gas=" + gas +
", gasUsed=" + gasUsed +
'}';
@@ -89,7 +89,7 @@ public static TxInternalBuilder builder() {
return new TxInternalBuilder();
}
- public static final class TxInternalBuilder {
+ public static class TxInternalBuilder {
private long blockNumber;
private LocalDateTime timeStamp;
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
index 4a2b624..b138c14 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java
@@ -143,48 +143,6 @@ public List getTransactions() {
}
//
- @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());
@@ -194,7 +152,7 @@ public static BlockProxyBuilder builder() {
return new BlockProxyBuilder();
}
- public static final class BlockProxyBuilder {
+ public static class BlockProxyBuilder {
private Long number;
private String hash;
@@ -353,4 +311,63 @@ public BlockProxy build() {
return blockProxy;
}
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ BlockProxy that = (BlockProxy) o;
+ return Objects.equals(number, that.number) && Objects.equals(_number, that._number) && Objects.equals(hash, that.hash)
+ && Objects.equals(parentHash, that.parentHash) && Objects.equals(stateRoot, that.stateRoot)
+ && Objects.equals(size, that.size) && Objects.equals(_size, that._size)
+ && Objects.equals(difficulty, that.difficulty) && Objects.equals(totalDifficulty, that.totalDifficulty)
+ && Objects.equals(timestamp, that.timestamp) && Objects.equals(_timestamp, that._timestamp)
+ && Objects.equals(miner, that.miner) && Objects.equals(nonce, that.nonce)
+ && Objects.equals(extraData, that.extraData) && Objects.equals(logsBloom, that.logsBloom)
+ && Objects.equals(mixHash, that.mixHash) && Objects.equals(gasUsed, that.gasUsed)
+ && Objects.equals(_gasUsed, that._gasUsed) && Objects.equals(gasLimit, that.gasLimit)
+ && Objects.equals(_gasLimit, that._gasLimit) && Objects.equals(sha3Uncles, that.sha3Uncles)
+ && Objects.equals(uncles, that.uncles) && Objects.equals(receiptsRoot, that.receiptsRoot)
+ && Objects.equals(transactionsRoot, that.transactionsRoot) && Objects.equals(transactions, that.transactions);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(number, number, hash, parentHash, stateRoot, size, size, difficulty, totalDifficulty, timestamp,
+ timestamp, miner, nonce, extraData, logsBloom, mixHash, gasUsed, gasUsed, gasLimit, gasLimit, sha3Uncles, uncles,
+ receiptsRoot, transactionsRoot, transactions);
+ }
+
+ @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/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java
index e6df01c..6c933c5 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java
@@ -95,46 +95,11 @@ public String getLogsBloom() {
}
//
- @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 {
+ public static class ReceiptProxyBuilder {
private String root;
private String from;
@@ -234,4 +199,50 @@ public ReceiptProxy build() {
return receiptProxy;
}
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ ReceiptProxy that = (ReceiptProxy) o;
+ return Objects.equals(root, that.root) && Objects.equals(from, that.from) && Objects.equals(to, that.to)
+ && Objects.equals(blockNumber, that.blockNumber) && Objects.equals(_blockNumber, that._blockNumber)
+ && Objects.equals(blockHash, that.blockHash) && Objects.equals(transactionHash, that.transactionHash)
+ && Objects.equals(transactionIndex, that.transactionIndex)
+ && Objects.equals(_transactionIndex, that._transactionIndex) && Objects.equals(gasUsed, that.gasUsed)
+ && Objects.equals(_gasUsed, that._gasUsed) && Objects.equals(cumulativeGasUsed, that.cumulativeGasUsed)
+ && Objects.equals(_cumulativeGasUsed, that._cumulativeGasUsed)
+ && Objects.equals(contractAddress, that.contractAddress) && Objects.equals(logs, that.logs)
+ && Objects.equals(logsBloom, that.logsBloom);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(root, from, to, blockNumber, blockNumber, blockHash, transactionHash, transactionIndex,
+ transactionIndex, gasUsed, gasUsed, cumulativeGasUsed, cumulativeGasUsed, contractAddress, logs, logsBloom);
+ }
+
+ @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/goodforgod/api/etherscan/model/proxy/TxProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java
index 70b4fd7..4372dde 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java
@@ -107,43 +107,6 @@ public Long getBlockNumber() {
}
//
- @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());
@@ -156,7 +119,7 @@ public static TxProxyBuilder builder() {
return new TxProxyBuilder();
}
- public static final class TxProxyBuilder {
+ public static class TxProxyBuilder {
private String to;
private String hash;
@@ -271,4 +234,53 @@ public TxProxy build() {
return txProxy;
}
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ TxProxy txProxy = (TxProxy) o;
+ return Objects.equals(to, txProxy.to) && Objects.equals(hash, txProxy.hash)
+ && Objects.equals(transactionIndex, txProxy.transactionIndex)
+ && Objects.equals(_transactionIndex, txProxy._transactionIndex) && Objects.equals(from, txProxy.from)
+ && Objects.equals(v, txProxy.v) && Objects.equals(input, txProxy.input) && Objects.equals(s, txProxy.s)
+ && Objects.equals(r, txProxy.r) && Objects.equals(nonce, txProxy.nonce) && Objects.equals(_nonce, txProxy._nonce)
+ && Objects.equals(value, txProxy.value) && Objects.equals(gas, txProxy.gas) && Objects.equals(_gas, txProxy._gas)
+ && Objects.equals(gasPrice, txProxy.gasPrice) && Objects.equals(_gasPrice, txProxy._gasPrice)
+ && Objects.equals(blockHash, txProxy.blockHash) && Objects.equals(blockNumber, txProxy.blockNumber)
+ && Objects.equals(_blockNumber, txProxy._blockNumber);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(to, hash, transactionIndex, transactionIndex, from, v, input, s, r, nonce, nonce, value, gas, gas,
+ gasPrice, gasPrice, blockHash, blockNumber, blockNumber);
+ }
+
+ @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/goodforgod/api/etherscan/model/proxy/utility/BaseProxyTO.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BaseProxyTO.java
index ef57193..24588c7 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BaseProxyTO.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BaseProxyTO.java
@@ -1,10 +1,12 @@
package io.goodforgod.api.etherscan.model.proxy.utility;
+import java.util.Objects;
+
/**
* @author GoodforGod
* @since 31.10.2018
*/
-abstract class BaseProxyTO {
+public abstract class BaseProxyTO {
private String id;
private String jsonrpc;
@@ -21,4 +23,28 @@ public String getJsonrpc() {
public ErrorProxyTO getError() {
return error;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ BaseProxyTO that = (BaseProxyTO) o;
+ return Objects.equals(id, that.id) && Objects.equals(jsonrpc, that.jsonrpc) && Objects.equals(error, that.error);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(id, jsonrpc, error);
+ }
+
+ @Override
+ public String toString() {
+ return "BaseProxyTO{" +
+ "id=" + id +
+ ", jsonrpc=" + jsonrpc +
+ ", error=" + error +
+ '}';
+ }
}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BlockProxyTO.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BlockProxyTO.java
index cf6c16b..b6bf1c1 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BlockProxyTO.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BlockProxyTO.java
@@ -1,6 +1,7 @@
package io.goodforgod.api.etherscan.model.proxy.utility;
import io.goodforgod.api.etherscan.model.proxy.BlockProxy;
+import java.util.Objects;
/**
* @author GoodforGod
@@ -13,4 +14,28 @@ public class BlockProxyTO extends BaseProxyTO {
public BlockProxy getResult() {
return result;
}
+
+ @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;
+ BlockProxyTO that = (BlockProxyTO) o;
+ return Objects.equals(result, that.result);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), result);
+ }
+
+ @Override
+ public String toString() {
+ return "BlockProxyTO{" +
+ "result=" + result +
+ '}';
+ }
}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/ErrorProxyTO.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/ErrorProxyTO.java
index 9b14cec..5aa1f0a 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/ErrorProxyTO.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/ErrorProxyTO.java
@@ -1,5 +1,7 @@
package io.goodforgod.api.etherscan.model.proxy.utility;
+import java.util.Objects;
+
/**
* @author GoodforGod
* @since 03.11.2018
@@ -16,4 +18,27 @@ public String getMessage() {
public String getCode() {
return code;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ ErrorProxyTO that = (ErrorProxyTO) o;
+ return Objects.equals(message, that.message) && Objects.equals(code, that.code);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(message, code);
+ }
+
+ @Override
+ public String toString() {
+ return "ErrorProxyTO{" +
+ "message=" + message +
+ ", code=" + code +
+ '}';
+ }
}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/StringProxyTO.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/StringProxyTO.java
index 489d87b..7afbf9c 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/StringProxyTO.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/StringProxyTO.java
@@ -1,5 +1,7 @@
package io.goodforgod.api.etherscan.model.proxy.utility;
+import java.util.Objects;
+
/**
* @author GoodforGod
* @since 31.10.2018
@@ -11,4 +13,28 @@ public class StringProxyTO extends BaseProxyTO {
public String getResult() {
return result;
}
+
+ @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;
+ StringProxyTO that = (StringProxyTO) o;
+ return Objects.equals(result, that.result);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), result);
+ }
+
+ @Override
+ public String toString() {
+ return "StringProxyTO{" +
+ "result=" + result +
+ '}';
+ }
}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxInfoProxyTO.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxInfoProxyTO.java
index 208cdbe..672585b 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxInfoProxyTO.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxInfoProxyTO.java
@@ -1,6 +1,7 @@
package io.goodforgod.api.etherscan.model.proxy.utility;
import io.goodforgod.api.etherscan.model.proxy.ReceiptProxy;
+import java.util.Objects;
/**
* @author GoodforGod
@@ -13,4 +14,28 @@ public class TxInfoProxyTO extends BaseProxyTO {
public ReceiptProxy getResult() {
return result;
}
+
+ @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;
+ TxInfoProxyTO that = (TxInfoProxyTO) o;
+ return Objects.equals(result, that.result);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), result);
+ }
+
+ @Override
+ public String toString() {
+ return "TxInfoProxyTO{" +
+ "result=" + result +
+ '}';
+ }
}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxProxyTO.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxProxyTO.java
index 0c084e7..45b885f 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxProxyTO.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxProxyTO.java
@@ -1,6 +1,7 @@
package io.goodforgod.api.etherscan.model.proxy.utility;
import io.goodforgod.api.etherscan.model.proxy.TxProxy;
+import java.util.Objects;
/**
* @author GoodforGod
@@ -13,4 +14,28 @@ public class TxProxyTO extends BaseProxyTO {
public TxProxy getResult() {
return result;
}
+
+ @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;
+ TxProxyTO txProxyTO = (TxProxyTO) o;
+ return Objects.equals(result, txProxyTO.result);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), result);
+ }
+
+ @Override
+ public String toString() {
+ return "TxProxyTO{" +
+ "result=" + result +
+ '}';
+ }
}
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
index 549bd47..f46a4e0 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryBuilderImpl.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryBuilderImpl.java
@@ -12,7 +12,7 @@
* @author GoodforGod
* @since 31.10.2018
*/
-final class LogQueryBuilderImpl implements LogQuery.Builder {
+public class LogQueryBuilderImpl implements LogQuery.Builder {
static final long MIN_BLOCK = 0;
static final long MAX_BLOCK = 99999999999999999L;
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
index ef66e72..6229c76 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryImpl.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryImpl.java
@@ -1,6 +1,7 @@
package io.goodforgod.api.etherscan.model.query;
import io.goodforgod.api.etherscan.LogsAPI;
+import java.util.Objects;
import org.jetbrains.annotations.NotNull;
/**
@@ -12,7 +13,7 @@
* @author GoodforGod
* @since 31.10.2018
*/
-final class LogQueryImpl implements LogQuery {
+public class LogQueryImpl implements LogQuery {
/**
* Final request parameter for api call
@@ -27,4 +28,26 @@ final class LogQueryImpl implements LogQuery {
public @NotNull String params() {
return params;
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ LogQueryImpl logQuery = (LogQueryImpl) o;
+ return Objects.equals(params, logQuery.params);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(params);
+ }
+
+ @Override
+ public String toString() {
+ return "LogQueryImpl{" +
+ "params=" + params + '\'' +
+ '}';
+ }
}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryParams.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryParams.java
index ac77ae8..12fb6d0 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryParams.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryParams.java
@@ -10,7 +10,7 @@
* @author GoodforGod
* @since 31.10.2018
*/
-final class LogQueryParams {
+public class LogQueryParams {
private LogQueryParams() {}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicQuadro.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicQuadro.java
index 7fdd9db..ff7bc8a 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicQuadro.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicQuadro.java
@@ -4,6 +4,7 @@
import io.goodforgod.api.etherscan.LogsAPI;
import io.goodforgod.api.etherscan.error.EtherScanLogQueryException;
+import java.util.Objects;
import org.jetbrains.annotations.NotNull;
/**
@@ -14,7 +15,7 @@
* @author GoodforGod
* @since 31.10.2018
*/
-public final class LogTopicQuadro implements LogTopicBuilder {
+public class LogTopicQuadro implements LogTopicBuilder {
private final String address;
private final long startBlock, endBlock;
@@ -96,4 +97,43 @@ public LogTopicQuadro setOpTopic1_3(LogOp topic1_3_opr) {
+ TOPIC_1_3_OPR_PARAM + topic1_2_opr.getOperation()
+ TOPIC_2_3_OPR_PARAM + topic0_2_opr.getOperation());
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ LogTopicQuadro that = (LogTopicQuadro) o;
+ return startBlock == that.startBlock && endBlock == that.endBlock && Objects.equals(address, that.address)
+ && Objects.equals(topic0, that.topic0) && Objects.equals(topic1, that.topic1)
+ && Objects.equals(topic2, that.topic2) && Objects.equals(topic3, that.topic3) && topic0_1_opr == that.topic0_1_opr
+ && topic1_2_opr == that.topic1_2_opr && topic2_3_opr == that.topic2_3_opr && topic0_2_opr == that.topic0_2_opr
+ && topic0_3_opr == that.topic0_3_opr && topic1_3_opr == that.topic1_3_opr;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(address, startBlock, endBlock, topic0, topic1, topic2, topic3, topic0_1_opr, topic1_2_opr,
+ topic2_3_opr, topic0_2_opr, topic0_3_opr, topic1_3_opr);
+ }
+
+ @Override
+ public String toString() {
+ return "LogTopicQuadro{" +
+ "address=" + address + '\'' +
+ ", startBlock=" + startBlock +
+ ", endBlock=" + endBlock +
+ ", topic0=" + topic0 + '\'' +
+ ", topic1=" + topic1 + '\'' +
+ ", topic2=" + topic2 + '\'' +
+ ", topic3=" + topic3 + '\'' +
+ ", topic0_1_opr=" + topic0_1_opr +
+ ", topic1_2_opr=" + topic1_2_opr +
+ ", topic2_3_opr=" + topic2_3_opr +
+ ", topic0_2_opr=" + topic0_2_opr +
+ ", topic0_3_opr=" + topic0_3_opr +
+ ", topic1_3_opr=" + topic1_3_opr +
+ '}';
+ }
}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicSingle.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicSingle.java
index a736ffa..58d7bda 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicSingle.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicSingle.java
@@ -4,6 +4,7 @@
import io.goodforgod.api.etherscan.LogsAPI;
import io.goodforgod.api.etherscan.error.EtherScanLogQueryException;
+import java.util.Objects;
import org.jetbrains.annotations.NotNull;
/**
@@ -14,7 +15,7 @@
* @author GoodforGod
* @since 31.10.2018
*/
-public final class LogTopicSingle implements LogTopicBuilder {
+public class LogTopicSingle implements LogTopicBuilder {
private final String address;
private final long startBlock, endBlock;
@@ -34,4 +35,30 @@ public final class LogTopicSingle implements LogTopicBuilder {
+ FROM_BLOCK_PARAM + startBlock + TO_BLOCK_PARAM + endBlock
+ TOPIC_0_PARAM + topic0);
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ LogTopicSingle that = (LogTopicSingle) o;
+ return startBlock == that.startBlock && endBlock == that.endBlock && Objects.equals(address, that.address)
+ && Objects.equals(topic0, that.topic0);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(address, startBlock, endBlock, topic0);
+ }
+
+ @Override
+ public String toString() {
+ return "LogTopicSingle{" +
+ "address=" + address + '\'' +
+ ", startBlock=" + startBlock +
+ ", endBlock=" + endBlock +
+ ", topic0=" + topic0 + '\'' +
+ '}';
+ }
}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTriple.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTriple.java
index ac9efb8..b5a25fe 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTriple.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTriple.java
@@ -4,6 +4,7 @@
import io.goodforgod.api.etherscan.LogsAPI;
import io.goodforgod.api.etherscan.error.EtherScanLogQueryException;
+import java.util.Objects;
import org.jetbrains.annotations.NotNull;
/**
@@ -14,7 +15,7 @@
* @author GoodforGod
* @since 31.10.2018
*/
-public final class LogTopicTriple implements LogTopicBuilder {
+public class LogTopicTriple implements LogTopicBuilder {
private final String address;
private final long startBlock, endBlock;
@@ -69,4 +70,37 @@ public LogTopicTriple setOpTopic1_2(LogOp topic1_2_opr) {
+ TOPIC_1_2_OPR_PARAM + topic1_2_opr.getOperation()
+ TOPIC_0_2_OPR_PARAM + topic0_2_opr.getOperation());
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ LogTopicTriple that = (LogTopicTriple) o;
+ return startBlock == that.startBlock && endBlock == that.endBlock && Objects.equals(address, that.address)
+ && Objects.equals(topic0, that.topic0) && Objects.equals(topic1, that.topic1)
+ && Objects.equals(topic2, that.topic2) && topic0_1_opr == that.topic0_1_opr && topic1_2_opr == that.topic1_2_opr
+ && topic0_2_opr == that.topic0_2_opr;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(address, startBlock, endBlock, topic0, topic1, topic2, topic0_1_opr, topic1_2_opr, topic0_2_opr);
+ }
+
+ @Override
+ public String toString() {
+ return "LogTopicTriple{" +
+ "address=" + address + '\'' +
+ ", startBlock=" + startBlock +
+ ", endBlock=" + endBlock +
+ ", topic0=" + topic0 + '\'' +
+ ", topic1=" + topic1 + '\'' +
+ ", topic2=" + topic2 + '\'' +
+ ", topic0_1_opr=" + topic0_1_opr +
+ ", topic1_2_opr=" + topic1_2_opr +
+ ", topic0_2_opr=" + topic0_2_opr +
+ '}';
+ }
}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTuple.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTuple.java
index 2ef2bba..e396ace 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTuple.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTuple.java
@@ -4,6 +4,7 @@
import io.goodforgod.api.etherscan.LogsAPI;
import io.goodforgod.api.etherscan.error.EtherScanLogQueryException;
+import java.util.Objects;
import org.jetbrains.annotations.NotNull;
/**
@@ -14,7 +15,7 @@
* @author GoodforGod
* @since 31.10.2018
*/
-public final class LogTopicTuple implements LogTopicBuilder {
+public class LogTopicTuple implements LogTopicBuilder {
private final String address;
private final long startBlock, endBlock;
@@ -50,4 +51,33 @@ public LogTopicTuple setOpTopic0_1(LogOp topic0_1_opr) {
+ TOPIC_1_PARAM + topic1
+ TOPIC_0_1_OPR_PARAM + topic0_1_opr.getOperation());
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (o == null || getClass() != o.getClass())
+ return false;
+ LogTopicTuple that = (LogTopicTuple) o;
+ return startBlock == that.startBlock && endBlock == that.endBlock && Objects.equals(address, that.address)
+ && Objects.equals(topic0, that.topic0) && Objects.equals(topic1, that.topic1)
+ && topic0_1_opr == that.topic0_1_opr;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(address, startBlock, endBlock, topic0, topic1, topic0_1_opr);
+ }
+
+ @Override
+ public String toString() {
+ return "LogTopicTuple{" +
+ "address=" + address + '\'' +
+ ", startBlock=" + startBlock +
+ ", endBlock=" + endBlock +
+ ", topic0=" + topic0 + '\'' +
+ ", topic1=" + topic1 + '\'' +
+ ", topic0_1_opr=" + topic0_1_opr +
+ '}';
+ }
}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/ContractCreationResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/ContractCreationResponseTO.java
new file mode 100644
index 0000000..e3766c3
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/response/ContractCreationResponseTO.java
@@ -0,0 +1,3 @@
+package io.goodforgod.api.etherscan.model.response;
+
+public class ContractCreationResponseTO extends BaseListResponseTO {}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/ContractCreationTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/ContractCreationTO.java
new file mode 100644
index 0000000..9e1551e
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/response/ContractCreationTO.java
@@ -0,0 +1,20 @@
+package io.goodforgod.api.etherscan.model.response;
+
+public class ContractCreationTO {
+
+ private String contractAddress;
+ private String contractCreator;
+ private String txHash;
+
+ public String getContractAddress() {
+ return contractAddress;
+ }
+
+ public String getContractCreator() {
+ return contractCreator;
+ }
+
+ public String getTxHash() {
+ return txHash;
+ }
+}
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
index 19fa0a1..8b01b3d 100644
--- a/src/main/java/io/goodforgod/api/etherscan/model/response/StringResponseTO.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/response/StringResponseTO.java
@@ -16,7 +16,7 @@ public static StringResponseBuilder builder() {
return new StringResponseBuilder();
}
- public static final class StringResponseBuilder {
+ public static class StringResponseBuilder {
private String status;
private String message;
diff --git a/src/main/java/io/goodforgod/api/etherscan/util/BasicUtils.java b/src/main/java/io/goodforgod/api/etherscan/util/BasicUtils.java
index 216ab62..db98bd4 100644
--- a/src/main/java/io/goodforgod/api/etherscan/util/BasicUtils.java
+++ b/src/main/java/io/goodforgod/api/etherscan/util/BasicUtils.java
@@ -17,7 +17,7 @@
* @author GoodforGod
* @since 28.10.2018
*/
-public final class BasicUtils {
+public class BasicUtils {
private BasicUtils() {}
@@ -149,4 +149,8 @@ public static List> partition(List list, int pairSize) {
return partitioned;
}
+
+ public static String toAddressParam(List addresses) {
+ return String.join(",", addresses);
+ }
}
diff --git a/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java b/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java
index 4b52c00..23df478 100644
--- a/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java
+++ b/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java
@@ -1,6 +1,7 @@
package io.goodforgod.api.etherscan;
import io.goodforgod.api.etherscan.manager.RequestQueueManager;
+import io.goodforgod.api.etherscan.util.BasicUtils;
import java.util.Map;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
@@ -15,6 +16,7 @@ public class ApiRunner extends Assertions {
static {
API_KEY = System.getenv().entrySet().stream()
.filter(e -> e.getKey().startsWith("ETHERSCAN_API_KEY"))
+ .filter(e -> !BasicUtils.isBlank(e.getValue()))
.map(Map.Entry::getValue)
.findFirst()
.orElse(DEFAULT_KEY);
@@ -23,10 +25,10 @@ public class ApiRunner extends Assertions {
? RequestQueueManager.anonymous()
: RequestQueueManager.planFree();
- API = EtherScanAPI.builder()
- .withApiKey(ApiRunner.API_KEY)
+ API = EtherScanAPI.builder(ApiRunner.API_KEY)
.withNetwork(EthNetworks.MAINNET)
.withQueue(queueManager)
+ .withRetryOnRateLimit(5)
.build();
}
@@ -34,6 +36,10 @@ public static EtherScanAPI getApi() {
return API;
}
+ public static String getKey() {
+ return API_KEY;
+ }
+
@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
index 36e23ec..636e752 100644
--- a/src/test/java/io/goodforgod/api/etherscan/EtherScanAPITests.java
+++ b/src/test/java/io/goodforgod/api/etherscan/EtherScanAPITests.java
@@ -4,7 +4,6 @@
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;
@@ -22,52 +21,26 @@ class EtherScanAPITests extends ApiRunner {
@Test
void validKey() {
String validKey = "YourKey";
- EtherScanAPI api = EtherScanAPI.builder().withApiKey(validKey).withNetwork(network).build();
+ EtherScanAPI api = EtherScanAPI.builder(validKey).withApiKey(validKey).withNetwork(network).build();
assertNotNull(api);
}
@Test
void emptyKey() {
- assertThrows(EtherScanKeyException.class, () -> EtherScanAPI.builder().withApiKey("").build());
+ assertThrows(EtherScanKeyException.class, () -> EtherScanAPI.builder("someKey").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);
+ () -> EtherScanAPI.builder("someKey").withApiKey(" ").withNetwork(network).build());
}
@Test
void timeout() throws InterruptedException {
TimeUnit.SECONDS.sleep(5);
Supplier supplier = () -> new UrlEthHttpClient(Duration.ofMillis(300), Duration.ofMillis(300));
- EtherScanAPI api = EtherScanAPI.builder()
+ EtherScanAPI api = EtherScanAPI.builder(ApiRunner.getKey())
.withNetwork(() -> URI.create("https://api-unknown.etherscan.io/api"))
.withHttpClient(supplier)
.build();
diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxsTests.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxsTests.java
index 653f62a..42e89c4 100644
--- a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxsTests.java
+++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxsTests.java
@@ -16,7 +16,7 @@ class AccountTxsTests extends ApiRunner {
void correct() {
List txs = getApi().account().txs("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9");
assertNotNull(txs);
- assertEquals(5, txs.size());
+ assertEquals(6, txs.size());
assertTxs(txs);
assertNotNull(txs.get(0).getTimeStamp());
assertNotNull(txs.get(0).getHash());
@@ -39,7 +39,7 @@ void correct() {
void correctStartBlock() {
List txs = getApi().account().txs("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9", 3892842);
assertNotNull(txs);
- assertEquals(4, txs.size());
+ assertEquals(5, txs.size());
assertTxs(txs);
}
diff --git a/src/test/java/io/goodforgod/api/etherscan/contract/ContractApiTests.java b/src/test/java/io/goodforgod/api/etherscan/contract/ContractApiTests.java
index 4fd0fdb..d1e4de4 100644
--- a/src/test/java/io/goodforgod/api/etherscan/contract/ContractApiTests.java
+++ b/src/test/java/io/goodforgod/api/etherscan/contract/ContractApiTests.java
@@ -3,6 +3,10 @@
import io.goodforgod.api.etherscan.ApiRunner;
import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException;
import io.goodforgod.api.etherscan.model.Abi;
+import io.goodforgod.api.etherscan.model.ContractCreation;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import org.junit.jupiter.api.Test;
/**
@@ -37,4 +41,46 @@ void correctParamWithEmptyExpectedResult() {
assertNotNull(abi);
assertTrue(abi.isVerified());
}
+
+ @Test
+ void correctContractCreation() {
+ List contractCreations = getApi().contract()
+ .contractCreation(Collections.singletonList("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413"));
+
+ assertEquals(1, contractCreations.size());
+ ContractCreation contractCreation = contractCreations.get(0);
+
+ assertEquals("0xbb9bc244d798123fde783fcc1c72d3bb8c189413", contractCreation.getContractAddress());
+ assertEquals("0x793ea9692ada1900fbd0b80fffec6e431fe8b391", contractCreation.getContractCreator());
+ assertEquals("0xe9ebfecc2fa10100db51a4408d18193b3ac504584b51a4e55bdef1318f0a30f9", contractCreation.getTxHash());
+ }
+
+ @Test
+ void correctMultipleContractCreation() {
+ List contractCreations = getApi().contract().contractCreation(
+ Arrays.asList("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413", "0x5EaC95ad5b287cF44E058dCf694419333b796123"));
+ assertEquals(2, contractCreations.size());
+
+ ContractCreation contractCreation1 = ContractCreation.builder()
+ .withContractAddress("0xbb9bc244d798123fde783fcc1c72d3bb8c189413")
+ .withContractCreator("0x793ea9692ada1900fbd0b80fffec6e431fe8b391")
+ .withTxHash("0xe9ebfecc2fa10100db51a4408d18193b3ac504584b51a4e55bdef1318f0a30f9")
+ .build();
+
+ ContractCreation contractCreation2 = ContractCreation.builder()
+ .withContractAddress("0x5eac95ad5b287cf44e058dcf694419333b796123")
+ .withContractCreator("0x7c675b7450e878e5af8550b41df42d134674e61f")
+ .withTxHash("0x79cdfec19e5a86d9022680a4d1c86d3d8cd76c21c01903a2f02c127a0a7dbfb3")
+ .build();
+
+ assertTrue(contractCreations.contains(contractCreation1));
+ assertTrue(contractCreations.contains(contractCreation2));
+ }
+
+ @Test
+ void contractCreationInvalidParamWithError() {
+ assertThrows(EtherScanInvalidAddressException.class,
+ () -> getApi().contract()
+ .contractCreation(Collections.singletonList("0xBBbc244D798123fDe783fCc1C72d3Bb8C189414")));
+ }
}
diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTests.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTests.java
index 363d5a2..53ed4cd 100644
--- a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTests.java
+++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTests.java
@@ -22,7 +22,6 @@ void correct() {
assertNotNull(proxy.getStateRoot());
assertNotNull(proxy.getSize());
assertNotNull(proxy.getDifficulty());
- assertNotNull(proxy.getTotalDifficulty());
assertNotNull(proxy.getTimeStamp());
assertNotNull(proxy.getMiner());
assertNotNull(proxy.getNonce());