diff --git a/.editorconfig b/.editorconfig
index 5b9451e..bd43bdc 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -8,14 +8,30 @@ root = true
end_of_line = lf
charset = utf-8
+# Json
+[*.json]
+indent_size = 2
+indent_style = space
+insert_final_newline = false
+trim_trailing_whitespace = true
+
# Yaml
[{*.yml, *.yaml}]
indent_size = 2
indent_style = space
+insert_final_newline = true
+trim_trailing_whitespace = true
# Property files
[*.properties]
indent_size = 2
indent_style = space
+insert_final_newline = true
+trim_trailing_whitespace = true
-
+# XML files
+[*.xml]
+indent_size = 4
+indent_style = space
+insert_final_newline = true
+trim_trailing_whitespace = true
diff --git a/.gitattributes b/.gitattributes
index ccc6fb5..856d969 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -2,9 +2,8 @@
# and leave all files detected as binary untouched.
* text=auto
-#
+
# The above will handle all files NOT found below
-#
# These files are text and should be normalized (Convert crlf => lf)
*.bash text eol=lf
*.css text diff=css
@@ -26,16 +25,36 @@
*.xml text
*.yml text eol=lf
+
# These files are binary and should be left untouched
# (binary is a macro for -text -diff)
-*.class binary
+# Archives
+*.7z binary
+*.br binary
+*.gz binary
+*.tar binary
+*.zip binary
+*.jar binary
+*.so binary
+*.war binary
*.dll binary
-*.ear binary
-*.gif binary
+
+# Documents
+*.pdf binary
+
+# Images
*.ico binary
-*.jar binary
+*.gif binary
*.jpg binary
*.jpeg binary
*.png binary
-*.so binary
-*.war binary
\ No newline at end of file
+*.psd binary
+*.webp binary
+
+# Fonts
+*.woff2 binary
+
+# Other
+*.exe binary
+*.class binary
+*.ear binary
diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml
index 613a39e..31c42f0 100644
--- a/.github/workflows/gradle.yml
+++ b/.github/workflows/gradle.yml
@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- java: [ '11' ]
+ java: [ '11', '17' ]
name: Java ${{ matrix.java }} setup
steps:
@@ -32,11 +32,19 @@ jobs:
run: ./gradlew spotlessCheck
- name: Test
+ if: matrix.java == '11'
run: ./gradlew test jacocoTestReport
env:
- API_KEY: ${{ secrets.API_KEY }}
+ ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY_1 }}
+
+ - name: Test
+ if: matrix.java == '17'
+ run: ./gradlew test jacocoTestReport
+ env:
+ ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY_2 }}
- name: SonarQube
+ if: matrix.java == '17'
run: ./gradlew sonarqube
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.gitignore b/.gitignore
index c48c7a6..b56b41b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,18 @@
-/.settings/
-.idea
-.idea/httpRequests
-*.iml
+### Package Files
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+### Gradle template
.gradle
-build
+build/
target/
+
+### Idea generated files
+.idea
+.settings/
+*.iml
+out/
diff --git a/README.md b/README.md
index c46a28f..dd244b5 100644
--- a/README.md
+++ b/README.md
@@ -1,22 +1,20 @@
# Java EtherScan API
+[](https://openjdk.org/projects/jdk8/)
[](https://github.com/GoodforGod/java-etherscan-api/actions?query=workflow%3A%22Java+CI%22)
[](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)
-[](https://jitpack.io/#GoodforGod/java-etherscan-api)
-[Etherscan.io](https://etherscan.io/apis) Java API implementation.
+[Etherscan.io](https://docs.etherscan.io/) Java API implementation.
-Library supports all available EtherScan *API* calls for all available *Ethereum Networks* for *etherscan.io*
+Library supports EtherScan *API* for all available *Ethereum Networks* for *etherscan.io*
## Dependency :rocket:
**Gradle**
```groovy
-dependencies {
- compile "com.github.goodforgod:java-etherscan-api:1.2.1"
-}
+implementation "com.github.goodforgod:java-etherscan-api:2.0.0"
```
**Maven**
@@ -24,7 +22,7 @@ dependencies {
com.github.goodforgod
java-etherscan-api
- 1.2.1
+ 2.0.0
```
@@ -42,143 +40,142 @@ dependencies {
- [Token](#token-api)
- [Version History](#version-history)
-## Mainnet and Testnets
+## MainNet and TestNets
+
+API support all Ethereum [default networks](https://docs.etherscan.io/getting-started/endpoint-urls):
+- [Mainnet](https://api.etherscan.io/)
+- [Goerli](https://api-goerli.etherscan.io/)
+- [Sepolia](https://api-sepolia.etherscan.io/)
-API support Ethereum: *[MAINNET](https://etherscan.io),
- [ROPSTEN](https://ropsten.etherscan.io),
- [KOVAN](https://kovan.etherscan.io),
- [RINKEBY](https://rinkeby.etherscan.io),
- [GORLI](https://goerli.etherscan.io),
- [TOBALABA](https://tobalaba.etherscan.com)* networks.
```java
-EtherScanApi api = new EtherScanApi(EthNetwork.MAINNET); // Default
-EtherScanApi apiRinkeby = new EtherScanApi(EthNetwork.RINKEBY);
-EtherScanApi apiRopsten = new EtherScanApi(EthNetwork.ROPSTEN);
-EtherScanApi apiKovan = new EtherScanApi("YourApiKey", EthNetwork.KOVAN);
+EtherScanAPI api = EtherScanAPI.build();
+EtherScanAPI apiGoerli = EtherScanAPI.builder().withNetwork(EthNetworks.GORLI).build();
+EtherScanAPI apiSepolia = EtherScanAPI.builder().withNetwork(EthNetworks.SEPOLIA).build();
+```
+
+### Custom Network
+
+In case you want to use API for other EtherScan compatible network, you can easily provide custom network with domain api URI.
+
+```java
+EtherScanAPI api = EtherScanAPI.builder()
+ .withNetwork(() -> URI.create("https://api-my-custom.etherscan.io/api"))
+ .build();
```
## Custom HttpClient
In case you need to set custom timeout, custom headers or better implementation for HttpClient,
-just implement **IHttpExecutor** by your self or initialize it with your values.
+just implement **EthHttpClient** by your self or initialize it with your values.
```java
-int connectionTimeout = 10000;
-int readTimeout = 7000;
-
-Supplier supplier = () -> new HttpExecutor(connectionTimeout);
-Supplier supplierFull = () -> new HttpExecutor(connectionTimeout, readTimeout);
-
-EtherScanApi api = new EtherScanApi(EthNetwork.RINKEBY, supplier);
-EtherScanApi apiWithKey = new EtherScanApi("YourApiKey", EthNetwork.MAINNET, supplierFull);
+Supplier ethHttpClientSupplier = () -> new UrlEthHttpClient(Duration.ofMillis(300), Duration.ofMillis(300));
+EtherScanAPI api = EtherScanAPI.builder()
+ .withHttpClient(supplier)
+ .build();
```
## API Examples
-You can read about all API methods on [Etherscan](https://etherscan.io/apis)
+You can read about all API methods on [Etherscan](https://docs.etherscan.io/api-endpoints/accounts)
*Library support all available EtherScan API.*
-You can use library *with or without* API key *([Check API request\sec restrictions when used without API key](https://ethereum.stackexchange.com/questions/34190/does-etherscan-require-the-use-of-an-api-key))*.
+You can use library *with or without* API key *([Check API request\sec restrictions when used without API key](https://docs.etherscan.io/getting-started/viewing-api-usage-statistics))*.
-Library will automatically limit requests up to **5 req/sec** when used *without* key.
+Library will automatically limit requests up to **1 requests in 5 seconds** when used *without* key and up to **5 requests in 1 seconds** when used with API KEY (free plan).
```java
-EtherScanApi api = new EtherScanApi();
-EtherScanApi api = new EtherScanApi("YourApiKey");
+EtherScanAPI.builder()
+ .withApiKey(ApiRunner.API_KEY)
+ .build();
```
Below are examples for each API category.
-### Account Api
+### Account API
**Get Ether Balance for a single Address**
-
```java
-EtherScanApi api = new EtherScanApi();
+EtherScanAPI api = EtherScanAPI.build();
Balance balance = api.account().balance("0x8d4426f94e42f721C7116E81d6688cd935cB3b4F");
```
-### Block Api
+### Block API
**Get uncles block for block height**
-
```java
-EtherScanApi api = new EtherScanApi();
+EtherScanAPI api = EtherScanAPI.build();
Optional uncles = api.block().uncles(200000);
```
-### Contract Api
+### Contract API
**Request contract ABI from [verified codes](https://etherscan.io/contractsVerified)**
```java
-EtherScanApi api = new EtherScanApi();
+EtherScanAPI api = EtherScanAPI.build();
Abi abi = api.contract().contractAbi("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413");
```
-### Logs Api
+### Logs API
**Get event logs for single topic**
-
```java
-EtherScanApi api = new EtherScanApi();
-LogQuery query = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c")
- .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545")
+EtherScanAPI api = EtherScanAPI.build();
+LogQuery query = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c")
+ .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545")
.build();
List logs = api.logs().logs(query);
```
**Get event logs for 3 topics with respectful operations**
-
```java
-EtherScanApi api = new EtherScanApi();
-LogQuery query = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000)
- .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
- "0x72657075746174696f6e00000000000000000000000000000000000000000000",
- "0x72657075746174696f6e00000000000000000000000000000000000000000000")
+EtherScanAPI api = EtherScanAPI.build();
+LogQuery query = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c")
+ .withBlockFrom(379224)
+ .withBlockTo(400000)
+ .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
+ "0x72657075746174696f6e00000000000000000000000000000000000000000000",
+ "0x72657075746174696f6e00000000000000000000000000000000000000000000")
.setOpTopic0_1(LogOp.AND)
- .setOpTopic0_2(LogOp.OR)
+ .setOpTopic0_2(null)
.setOpTopic1_2(LogOp.AND)
.build();
List logs = api.logs().logs(query);
```
-### Proxy Api
-
-**Get tx detailds with proxy endpoint**
+### Proxy API
+**Get tx details with proxy endpoint**
```java
-EtherScanApi api = new EtherScanApi(EthNetwork.MAINNET);
+EtherScanAPI api = EtherScanAPI.build();
Optional tx = api.proxy().tx("0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1");
```
**Get block info with proxy endpoint**
-
```java
-EtherScanApi api = new EtherScanApi(EthNetwork.MAINNET);
+EtherScanAPI api = EtherScanAPI.build();
Optional block = api.proxy().block(15215);
```
-### Stats Api
+### Stats API
**Statistic about last price**
-
```java
-EtherScanApi api = new EtherScanApi();
-Price price = api.stats().lastPrice();
+EtherScanAPI api = EtherScanAPI.build();
+Price price = api.stats().priceLast();
```
-### Transaction Api
+### Transaction API
**Request receipt status for tx**
-
```java
-EtherScanApi api = new EtherScanApi();
+EtherScanAPI api = EtherScanAPI.build();
Optional status = api.txs().receiptStatus("0x513c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76");
```
-### Token Api
+### Token API
-You can read about token API [here](https://etherscan.io/apis#tokens)
+You can read about token API [here](https://docs.etherscan.io/api-endpoints/tokens)
Token API methods migrated to [Account](#account-api) & [Stats](#stats-api) respectfully.
diff --git a/build.gradle b/build.gradle
index 70ed3fa..3d766c2 100644
--- a/build.gradle
+++ b/build.gradle
@@ -4,7 +4,7 @@ plugins {
id "maven-publish"
id "org.sonarqube" version "3.3"
- id "com.diffplug.spotless" version "5.14.3"
+ id "com.diffplug.spotless" version "6.12.0"
}
repositories {
@@ -18,34 +18,21 @@ version = artifactVersion
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
-spotless {
- java {
- encoding "UTF-8"
- removeUnusedImports()
- eclipse().configFile "${projectDir}/config/codestyle.xml"
- }
-}
-
-sonarqube {
- properties {
- property "sonar.host.url", "https://sonarcloud.io"
- property "sonar.organization", "goodforgod"
- property "sonar.projectKey", "GoodforGod_java-etherscan-api"
- }
-}
-
dependencies {
- implementation "org.jetbrains:annotations:22.0.0"
- implementation "com.google.code.gson:gson:2.8.9"
+ compileOnly "org.jetbrains:annotations:23.0.0"
+ implementation "io.goodforgod:gson-configuration:2.0.0"
- testImplementation "junit:junit:4.13.2"
+ testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.9.3"
+ testImplementation "org.junit.jupiter:junit-jupiter-api:5.9.3"
+ testImplementation "org.junit.jupiter:junit-jupiter-params:5.9.3"
}
test {
- useJUnit()
+ useJUnitPlatform()
testLogging {
events("passed", "skipped", "failed")
exceptionFormat("full")
+ showStandardStreams(false)
}
reports {
@@ -54,6 +41,23 @@ test {
}
}
+spotless {
+ java {
+ encoding("UTF-8")
+ importOrder()
+ removeUnusedImports()
+ eclipse("4.21.0").configFile("${rootDir}/config/codestyle.xml")
+ }
+}
+
+sonarqube {
+ properties {
+ property "sonar.host.url", "https://sonarcloud.io"
+ property "sonar.organization", "goodforgod"
+ property "sonar.projectKey", "GoodforGod_$artifactId"
+ }
+}
+
publishing {
publications {
mavenJava(MavenPublication) {
@@ -61,12 +65,12 @@ publishing {
pom {
name = "Java Etherscan API"
- url = "https://github.com/GoodforGod/java-etherscan-api"
+ url = "https://github.com/GoodforGod/$artifactId"
description = "Library is a wrapper for EtherScan API."
license {
name = "MIT License"
- url = "https://github.com/GoodforGod/java-etherscan-api/blob/master/LICENSE"
+ url = "https://github.com/GoodforGod/$artifactId/blob/master/LICENSE"
distribution = "repo"
}
@@ -78,9 +82,9 @@ publishing {
}
scm {
- connection = "scm:git:git://github.com/GoodforGod/java-etherscan-api.git"
- developerConnection = "scm:git:ssh://GoodforGod/java-etherscan-api.git"
- url = "https://github.com/GoodforGod/java-etherscan-api/tree/master"
+ connection = "scm:git:git://github.com/GoodforGod/${artifactId}.git"
+ developerConnection = "scm:git:ssh://GoodforGod/${artifactId}.git"
+ url = "https://github.com/GoodforGod/$artifactId/tree/master"
}
}
}
diff --git a/config/codestyle.xml b/config/codestyle.xml
index a90c4f5..ad0c929 100644
--- a/config/codestyle.xml
+++ b/config/codestyle.xml
@@ -1,156 +1,95 @@
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
+
+
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
+
-
-
-
+
-
-
-
-
-
-
-
-
-
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
+
@@ -158,189 +97,292 @@
-
-
-
-
-
-
+
+
-
-
-
+
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
+
-
-
-
-
+
-
+
-
-
-
-
-
-
-
-
+
-
+
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
-
-
-
diff --git a/gradle.properties b/gradle.properties
index a6ba485..821da06 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,6 +1,6 @@
groupId=com.github.goodforgod
artifactId=java-etherscan-api
-artifactVersion=1.2.1
+artifactVersion=2.0.0
##### GRADLE #####
@@ -8,4 +8,9 @@ org.gradle.daemon=true
org.gradle.parallel=true
org.gradle.configureondemand=true
org.gradle.caching=true
-org.gradle.jvmargs=-Dfile.encoding=UTF-8
\ No newline at end of file
+org.gradle.jvmargs=-Dfile.encoding=UTF-8 \
+ --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \
+ --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \
+ --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \
+ --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \
+ --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index ffed3a2..070cb70 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index 744e882..1b6c787 100755
--- a/gradlew
+++ b/gradlew
@@ -1,7 +1,7 @@
-#!/usr/bin/env sh
+#!/bin/sh
#
-# Copyright 2015 the original author or authors.
+# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,67 +17,101 @@
#
##############################################################################
-##
-## Gradle start up script for UN*X
-##
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
##############################################################################
# Attempt to set APP_HOME
+
# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >/dev/null
-APP_HOME="`pwd -P`"
-cd "$SAVED" >/dev/null
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
+APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
+MAX_FD=maximum
warn () {
echo "$*"
-}
+} >&2
die () {
echo
echo "$*"
echo
exit 1
-}
+} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MSYS* | MINGW* )
- msys=true
- ;;
- NONSTOP* )
- nonstop=true
- ;;
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
@@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
+ JAVACMD=$JAVA_HOME/jre/sh/java
else
- JAVACMD="$JAVA_HOME/bin/java"
+ JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
- JAVACMD="java"
+ JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
@@ -106,80 +140,95 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
fi
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# For Cygwin or MSYS, switch paths to Windows format before running java
-if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
-
- JAVACMD=`cygpath --unix "$JAVACMD"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRADLE_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
- fi
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
+
# Now convert the arguments - kludge to limit ourselves to /bin/sh
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
fi
- i=`expr $i + 1`
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
done
- case $i in
- 0) set -- ;;
- 1) set -- "$args0" ;;
- 2) set -- "$args0" "$args1" ;;
- 3) set -- "$args0" "$args1" "$args2" ;;
- 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
- esac
fi
-# Escape application args
-save () {
- for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
- echo " "
-}
-APP_ARGS=`save "$@"`
+# Collect all arguments for the java command;
+# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+# shell script including quotes and variable substitutions, so put them in
+# double quotes to make sure that they get re-expanded; and
+# * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
-# Collect all arguments for the java command, following the shell quoting and substitution rules
-eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
exec "$JAVACMD" "$@"
diff --git a/src/main/java/io/api/etherscan/core/IAccountApi.java b/src/main/java/io/api/etherscan/core/IAccountApi.java
deleted file mode 100644
index 25254aa..0000000
--- a/src/main/java/io/api/etherscan/core/IAccountApi.java
+++ /dev/null
@@ -1,140 +0,0 @@
-package io.api.etherscan.core;
-
-import io.api.etherscan.error.ApiException;
-import io.api.etherscan.model.*;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.List;
-
-/**
- * EtherScan - API Descriptions https://etherscan.io/apis#accounts
- *
- * @author GoodforGod
- * @since 28.10.2018
- */
-public interface IAccountApi {
-
- /**
- * Address ETH balance
- *
- * @param address get balance for
- * @return balance
- * @throws ApiException parent exception class
- */
- @NotNull
- Balance balance(String address) throws ApiException;
-
- /**
- * ERC20 token balance for address
- *
- * @param address get balance for
- * @param contract token contract
- * @return token balance for address
- * @throws ApiException parent exception class
- */
- @NotNull
- TokenBalance balance(String address, String contract) throws ApiException;
-
- /**
- * Maximum 20 address for single batch request If address MORE THAN 20, then there will be more than
- * 1 request performed
- *
- * @param addresses addresses to get balances for
- * @return list of balances
- * @throws ApiException parent exception class
- */
- @NotNull
- List balances(List addresses) throws ApiException;
-
- /**
- * All txs for given address
- *
- * @param address get txs for
- * @param startBlock tx from this blockNumber
- * @param endBlock tx to this blockNumber
- * @return txs for address
- * @throws ApiException parent exception class
- */
- @NotNull
- List txs(String address, long startBlock, long endBlock) throws ApiException;
-
- @NotNull
- List txs(String address, long startBlock) throws ApiException;
-
- @NotNull
- List txs(String address) throws ApiException;
-
- /**
- * All internal txs for given address
- *
- * @param address get txs for
- * @param startBlock tx from this blockNumber
- * @param endBlock tx to this blockNumber
- * @return txs for address
- * @throws ApiException parent exception class
- */
- @NotNull
- List txsInternal(String address, long startBlock, long endBlock) throws ApiException;
-
- @NotNull
- List txsInternal(String address, long startBlock) throws ApiException;
-
- @NotNull
- List txsInternal(String address) throws ApiException;
-
- /**
- * All internal tx for given transaction hash
- *
- * @param txhash transaction hash
- * @return internal txs list
- * @throws ApiException parent exception class
- */
- @NotNull
- List txsInternalByHash(String txhash) throws ApiException;
-
- /**
- * All ERC-20 token txs for given address
- *
- * @param address get txs for
- * @param startBlock tx from this blockNumber
- * @param endBlock tx to this blockNumber
- * @return txs for address
- * @throws ApiException parent exception class
- */
- @NotNull
- List txsToken(String address, long startBlock, long endBlock) throws ApiException;
-
- @NotNull
- List txsToken(String address, long startBlock) throws ApiException;
-
- @NotNull
- List txsToken(String address) throws ApiException;
-
- /**
- * All ERC-721 (NFT) token txs for given address
- *
- * @param address get txs for
- * @param startBlock tx from this blockNumber
- * @param endBlock tx to this blockNumber
- * @return txs for address
- * @throws ApiException parent exception class
- */
- @NotNull
- List txsNftToken(String address, long startBlock, long endBlock) throws ApiException;
-
- @NotNull
- List txsNftToken(String address, long startBlock) throws ApiException;
-
- @NotNull
- List txsNftToken(String address) throws ApiException;
-
- /**
- * All blocks mined by address
- *
- * @param address address to search for
- * @return blocks mined
- * @throws ApiException parent exception class
- */
- @NotNull
- List minedBlocks(String address) throws ApiException;
-}
diff --git a/src/main/java/io/api/etherscan/core/IBlockApi.java b/src/main/java/io/api/etherscan/core/IBlockApi.java
deleted file mode 100644
index 7381ac0..0000000
--- a/src/main/java/io/api/etherscan/core/IBlockApi.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package io.api.etherscan.core;
-
-import io.api.etherscan.error.ApiException;
-import io.api.etherscan.model.UncleBlock;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.Optional;
-
-/**
- * EtherScan - API Descriptions https://etherscan.io/apis#blocks
- *
- * @author GoodforGod
- * @since 30.10.2018
- */
-public interface IBlockApi {
-
- /**
- * Return uncle blocks
- *
- * @param blockNumber block number form 0 to last
- * @return optional uncle blocks
- * @throws ApiException parent exception class
- */
- @NotNull
- Optional uncles(long blockNumber) throws ApiException;
-}
diff --git a/src/main/java/io/api/etherscan/core/IContractApi.java b/src/main/java/io/api/etherscan/core/IContractApi.java
deleted file mode 100644
index 3e9388d..0000000
--- a/src/main/java/io/api/etherscan/core/IContractApi.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package io.api.etherscan.core;
-
-import io.api.etherscan.error.ApiException;
-import io.api.etherscan.model.Abi;
-import org.jetbrains.annotations.NotNull;
-
-/**
- * EtherScan - API Descriptions https://etherscan.io/apis#contracts
- *
- * @author GoodforGod
- * @since 28.10.2018
- */
-public interface IContractApi {
-
- /**
- * Get Verified Contract Sources
- *
- * @param address to verify
- * @return ABI verified
- * @throws ApiException parent exception class
- */
- @NotNull
- Abi contractAbi(String address) throws ApiException;
-}
diff --git a/src/main/java/io/api/etherscan/core/ILogsApi.java b/src/main/java/io/api/etherscan/core/ILogsApi.java
deleted file mode 100644
index 37c5eac..0000000
--- a/src/main/java/io/api/etherscan/core/ILogsApi.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package io.api.etherscan.core;
-
-import io.api.etherscan.error.ApiException;
-import io.api.etherscan.model.Log;
-import io.api.etherscan.model.query.impl.LogQuery;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.List;
-
-/**
- * EtherScan - API Descriptions https://etherscan.io/apis#logs
- *
- * @author GoodforGod
- * @since 30.10.2018
- */
-public interface ILogsApi {
-
- /**
- * alternative to the native eth_getLogs Read at EtherScan API description for full info!
- *
- * @param query build log query
- * @return logs according to query
- * @throws ApiException parent exception class
- *
- * @see io.api.etherscan.model.query.impl.LogQueryBuilder
- */
- @NotNull
- List logs(LogQuery query) throws ApiException;
-}
diff --git a/src/main/java/io/api/etherscan/core/IStatisticApi.java b/src/main/java/io/api/etherscan/core/IStatisticApi.java
deleted file mode 100644
index 1b7ef59..0000000
--- a/src/main/java/io/api/etherscan/core/IStatisticApi.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package io.api.etherscan.core;
-
-import io.api.etherscan.error.ApiException;
-import io.api.etherscan.model.Price;
-import io.api.etherscan.model.Supply;
-import org.jetbrains.annotations.NotNull;
-
-import java.math.BigInteger;
-
-/**
- * EtherScan - API Descriptions https://etherscan.io/apis#stats
- *
- * @author GoodforGod
- * @since 30.10.2018
- */
-public interface IStatisticApi {
-
- /**
- * ERC20 token total Supply
- *
- * @param contract contract address
- * @return token supply for specified contract
- * @throws ApiException parent exception class
- */
- @NotNull
- BigInteger supply(String contract) throws ApiException;
-
- /**
- * Eth total supply
- *
- * @return total ETH supply for moment
- * @throws ApiException parent exception class
- */
- @NotNull
- Supply supply() throws ApiException;
-
- /**
- * Eth last USD and BTC price
- *
- * @return last usd/btc price for ETH
- * @throws ApiException parent exception class
- */
- @NotNull
- Price lastPrice() throws ApiException;
-}
diff --git a/src/main/java/io/api/etherscan/core/impl/AccountApiProvider.java b/src/main/java/io/api/etherscan/core/impl/AccountApiProvider.java
deleted file mode 100644
index 77d8b88..0000000
--- a/src/main/java/io/api/etherscan/core/impl/AccountApiProvider.java
+++ /dev/null
@@ -1,268 +0,0 @@
-package io.api.etherscan.core.impl;
-
-import io.api.etherscan.core.IAccountApi;
-import io.api.etherscan.error.ApiException;
-import io.api.etherscan.error.EtherScanException;
-import io.api.etherscan.executor.IHttpExecutor;
-import io.api.etherscan.manager.IQueueManager;
-import io.api.etherscan.model.*;
-import io.api.etherscan.model.utility.*;
-import io.api.etherscan.util.BasicUtils;
-import org.jetbrains.annotations.NotNull;
-
-import java.math.BigInteger;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * Account API Implementation
- *
- * @see IAccountApi
- *
- * @author GoodforGod
- * @since 28.10.2018
- */
-public class AccountApiProvider extends BasicProvider implements IAccountApi {
-
- private static final int OFFSET_MAX = 10000;
-
- private static final String ACT_BALANCE_ACTION = ACT_PREFIX + "balance";
- private static final String ACT_TOKEN_BALANCE_PARAM = ACT_PREFIX + "tokenbalance";
- private static final String ACT_BALANCE_MULTI_ACTION = ACT_PREFIX + "balancemulti";
- private static final String ACT_TX_ACTION = ACT_PREFIX + "txlist";
- private static final String ACT_TX_INTERNAL_ACTION = ACT_PREFIX + "txlistinternal";
- private static final String ACT_TX_TOKEN_ACTION = ACT_PREFIX + "tokentx";
- private static final String ACT_TX_NFT_TOKEN_ACTION = ACT_PREFIX + "tokennfttx";
- private static final String ACT_MINED_ACTION = ACT_PREFIX + "getminedblocks";
-
- private static final String BLOCK_TYPE_PARAM = "&blocktype=blocks";
- private static final String CONTRACT_PARAM = "&contractaddress=";
- private static final String START_BLOCK_PARAM = "&startblock=";
- private static final String TAG_LATEST_PARAM = "&tag=latest";
- private static final String END_BLOCK_PARAM = "&endblock=";
- private static final String SORT_DESC_PARAM = "&sort=desc";
- private static final String SORT_ASC_PARAM = "&sort=asc";
- private static final String ADDRESS_PARAM = "&address=";
- private static final String TXHASH_PARAM = "&txhash=";
- private static final String OFFSET_PARAM = "&offset=";
- private static final String PAGE_PARAM = "&page=";
-
- AccountApiProvider(final IQueueManager queueManager,
- final String baseUrl,
- final IHttpExecutor executor) {
- super(queueManager, "account", baseUrl, executor);
- }
-
- @NotNull
- @Override
- public Balance balance(final String address) throws ApiException {
- BasicUtils.validateAddress(address);
-
- final String urlParams = ACT_BALANCE_ACTION + TAG_LATEST_PARAM + ADDRESS_PARAM + address;
- final StringResponseTO response = getRequest(urlParams, StringResponseTO.class);
- if (response.getStatus() != 1)
- throw new EtherScanException(response);
-
- return new Balance(address, new BigInteger(response.getResult()));
- }
-
- @NotNull
- @Override
- public TokenBalance balance(final String address, final String contract) throws ApiException {
- BasicUtils.validateAddress(address);
- BasicUtils.validateAddress(contract);
-
- final String urlParams = ACT_TOKEN_BALANCE_PARAM + ADDRESS_PARAM + address + CONTRACT_PARAM + contract;
- final StringResponseTO response = getRequest(urlParams, StringResponseTO.class);
- if (response.getStatus() != 1)
- throw new EtherScanException(response);
-
- return new TokenBalance(address, new BigInteger(response.getResult()), contract);
- }
-
- @NotNull
- @Override
- public List balances(final List addresses) throws ApiException {
- if (BasicUtils.isEmpty(addresses))
- return Collections.emptyList();
-
- BasicUtils.validateAddresses(addresses);
-
- // Maximum addresses in batch request - 20
- final List balances = new ArrayList<>();
- final List> addressesAsBatches = BasicUtils.partition(addresses, 20);
-
- for (final List batch : addressesAsBatches) {
- final String urlParams = ACT_BALANCE_MULTI_ACTION + TAG_LATEST_PARAM + ADDRESS_PARAM + toAddressParam(batch);
- final BalanceResponseTO response = getRequest(urlParams, BalanceResponseTO.class);
- if (response.getStatus() != 1)
- throw new EtherScanException(response);
-
- if (!BasicUtils.isEmpty(response.getResult()))
- balances.addAll(response.getResult().stream()
- .map(Balance::of)
- .collect(Collectors.toList()));
- }
-
- return balances;
- }
-
- private String toAddressParam(final List addresses) {
- return addresses.stream().collect(Collectors.joining(","));
- }
-
- @NotNull
- @Override
- public List txs(final String address) throws ApiException {
- return txs(address, MIN_START_BLOCK);
- }
-
- @NotNull
- @Override
- public List txs(final String address, final long startBlock) throws ApiException {
- return txs(address, startBlock, MAX_END_BLOCK);
- }
-
- @NotNull
- @Override
- public List txs(final String address, final long startBlock, final long endBlock) throws ApiException {
- BasicUtils.validateAddress(address);
- final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock);
-
- final String offsetParam = PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX;
- final String blockParam = START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end();
- final String urlParams = ACT_TX_ACTION + offsetParam + ADDRESS_PARAM + address + blockParam + SORT_ASC_PARAM;
-
- return getRequestUsingOffset(urlParams, TxResponseTO.class);
- }
-
- /**
- * Generic search for txs using offset api param To avoid 10k limit per response
- *
- * @param urlParams Url params for #getRequest()
- * @param tClass responseListTO class
- * @param responseTO list T type
- * @param responseListTO type
- * @return List of T values
- */
- private List getRequestUsingOffset(final String urlParams,
- Class tClass) throws ApiException {
- final List result = new ArrayList<>();
- int page = 1;
- while (true) {
- final String formattedUrl = String.format(urlParams, page++);
- final R response = getRequest(formattedUrl, tClass);
- BasicUtils.validateTxResponse(response);
- if (BasicUtils.isEmpty(response.getResult()))
- break;
-
- result.addAll(response.getResult());
- if (response.getResult().size() < OFFSET_MAX)
- break;
- }
-
- return result;
- }
-
- @NotNull
- @Override
- public List txsInternal(final String address) throws ApiException {
- return txsInternal(address, MIN_START_BLOCK);
- }
-
- @NotNull
- @Override
- public List txsInternal(final String address, final long startBlock) throws ApiException {
- return txsInternal(address, startBlock, MAX_END_BLOCK);
- }
-
- @NotNull
- @Override
- public List txsInternal(final String address, final long startBlock, final long endBlock) throws ApiException {
- BasicUtils.validateAddress(address);
- final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock);
-
- final String offsetParam = PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX;
- final String blockParam = START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end();
- final String urlParams = ACT_TX_INTERNAL_ACTION + offsetParam + ADDRESS_PARAM + address + blockParam + SORT_ASC_PARAM;
-
- return getRequestUsingOffset(urlParams, TxInternalResponseTO.class);
- }
-
- @NotNull
- @Override
- public List txsInternalByHash(final String txhash) throws ApiException {
- BasicUtils.validateTxHash(txhash);
-
- final String urlParams = ACT_TX_INTERNAL_ACTION + TXHASH_PARAM + txhash;
- final TxInternalResponseTO response = getRequest(urlParams, TxInternalResponseTO.class);
- BasicUtils.validateTxResponse(response);
-
- return BasicUtils.isEmpty(response.getResult())
- ? Collections.emptyList()
- : response.getResult();
- }
-
- @NotNull
- @Override
- public List txsToken(final String address) throws ApiException {
- return txsToken(address, MIN_START_BLOCK);
- }
-
- @NotNull
- @Override
- public List txsToken(final String address, final long startBlock) throws ApiException {
- return txsToken(address, startBlock, MAX_END_BLOCK);
- }
-
- @NotNull
- @Override
- public List txsToken(final String address, final long startBlock, final long endBlock) throws ApiException {
- BasicUtils.validateAddress(address);
- final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock);
-
- final String offsetParam = PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX;
- final String blockParam = START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end();
- final String urlParams = ACT_TX_TOKEN_ACTION + offsetParam + ADDRESS_PARAM + address + blockParam + SORT_ASC_PARAM;
-
- return getRequestUsingOffset(urlParams, TxTokenResponseTO.class);
- }
-
- @NotNull
- @Override
- public List txsNftToken(String address) throws ApiException {
- return txsNftToken(address, MIN_START_BLOCK);
- }
-
- @NotNull
- @Override
- public List txsNftToken(String address, long startBlock) throws ApiException {
- return txsNftToken(address, startBlock, MAX_END_BLOCK);
- }
-
- @NotNull
- @Override
- public List txsNftToken(String address, long startBlock, long endBlock) throws ApiException {
- BasicUtils.validateAddress(address);
- final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock);
-
- final String offsetParam = PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX;
- final String blockParam = START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end();
- final String urlParams = ACT_TX_NFT_TOKEN_ACTION + offsetParam + ADDRESS_PARAM + address + blockParam + SORT_ASC_PARAM;
-
- return getRequestUsingOffset(urlParams, TxTokenResponseTO.class);
- }
-
- @NotNull
- @Override
- public List minedBlocks(final String address) throws ApiException {
- BasicUtils.validateAddress(address);
-
- final String offsetParam = PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX;
- final String urlParams = ACT_MINED_ACTION + offsetParam + BLOCK_TYPE_PARAM + ADDRESS_PARAM + address;
-
- return getRequestUsingOffset(urlParams, BlockResponseTO.class);
- }
-}
diff --git a/src/main/java/io/api/etherscan/core/impl/BasicProvider.java b/src/main/java/io/api/etherscan/core/impl/BasicProvider.java
deleted file mode 100644
index b36f406..0000000
--- a/src/main/java/io/api/etherscan/core/impl/BasicProvider.java
+++ /dev/null
@@ -1,100 +0,0 @@
-package io.api.etherscan.core.impl;
-
-import com.google.gson.*;
-import io.api.etherscan.error.ApiException;
-import io.api.etherscan.error.EtherScanException;
-import io.api.etherscan.error.ParseException;
-import io.api.etherscan.error.RateLimitException;
-import io.api.etherscan.executor.IHttpExecutor;
-import io.api.etherscan.manager.IQueueManager;
-import io.api.etherscan.model.utility.StringResponseTO;
-import io.api.etherscan.util.BasicUtils;
-
-import java.time.LocalDate;
-import java.time.LocalDateTime;
-import java.util.Map;
-
-/**
- * Base provider for API Implementations
- *
- * @author GoodforGod
- * @see EtherScanApi
- * @since 28.10.2018
- */
-abstract class BasicProvider {
-
- static final int MAX_END_BLOCK = Integer.MAX_VALUE;
- static final int MIN_START_BLOCK = 0;
-
- static final String ACT_PREFIX = "&action=";
-
- private final String module;
- private final String baseUrl;
- private final IHttpExecutor executor;
- private final IQueueManager queue;
- private final Gson gson;
-
- BasicProvider(final IQueueManager queue,
- final String module,
- final String baseUrl,
- final IHttpExecutor executor) {
- this.queue = queue;
- this.module = "&module=" + module;
- this.baseUrl = baseUrl;
- this.executor = executor;
- this.gson = new GsonBuilder()
- .registerTypeAdapter(LocalDateTime.class, (JsonSerializer) (src, t, c) -> new JsonPrimitive(""))
- .registerTypeAdapter(LocalDate.class, (JsonSerializer) (src, t, context) -> new JsonPrimitive(""))
- .registerTypeAdapter(LocalDateTime.class, (JsonDeserializer) (json, t, c) -> null)
- .registerTypeAdapter(LocalDate.class, (JsonDeserializer) (json, t, c) -> null)
- .create();
- }
-
- T convert(final String json, final Class tClass) {
- try {
- final T t = gson.fromJson(json, tClass);
- if (t instanceof StringResponseTO && ((StringResponseTO) t).getResult().startsWith("Max rate limit reached")) {
- throw new RateLimitException(((StringResponseTO) t).getResult());
- }
-
- return t;
- } catch (Exception e) {
- try {
- final Map map = gson.fromJson(json, Map.class);
- final Object result = map.get("result");
- if (result instanceof String && ((String) result).startsWith("Max rate limit reached"))
- throw new RateLimitException(((String) result));
-
- throw new ParseException(e.getMessage() + ", for response: " + json, e.getCause(), json);
- } catch (ApiException ex) {
- throw ex;
- } catch (Exception ex) {
- throw new ParseException(e.getMessage() + ", for response: " + json, e.getCause(), json);
- }
- }
- }
-
- String getRequest(final String urlParameters) {
- queue.takeTurn();
- final String url = baseUrl + module + urlParameters;
- final String result = executor.get(url);
- if (BasicUtils.isEmpty(result))
- throw new EtherScanException("Server returned null value for GET request at URL - " + url);
-
- return result;
- }
-
- String postRequest(final String urlParameters, final String dataToPost) {
- queue.takeTurn();
- final String url = baseUrl + module + urlParameters;
- return executor.post(url, dataToPost);
- }
-
- T getRequest(final String urlParameters, final Class tClass) {
- return convert(getRequest(urlParameters), tClass);
- }
-
- T postRequest(final String urlParameters, final String dataToPost, final Class tClass) {
- return convert(postRequest(urlParameters, dataToPost), tClass);
- }
-}
diff --git a/src/main/java/io/api/etherscan/core/impl/BlockApiProvider.java b/src/main/java/io/api/etherscan/core/impl/BlockApiProvider.java
deleted file mode 100644
index 9f386a7..0000000
--- a/src/main/java/io/api/etherscan/core/impl/BlockApiProvider.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package io.api.etherscan.core.impl;
-
-import io.api.etherscan.core.IBlockApi;
-import io.api.etherscan.error.ApiException;
-import io.api.etherscan.executor.IHttpExecutor;
-import io.api.etherscan.manager.IQueueManager;
-import io.api.etherscan.model.UncleBlock;
-import io.api.etherscan.model.utility.UncleBlockResponseTO;
-import io.api.etherscan.util.BasicUtils;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.Optional;
-
-/**
- * Block API Implementation
- *
- * @see IBlockApi
- *
- * @author GoodforGod
- * @since 28.10.2018
- */
-public class BlockApiProvider extends BasicProvider implements IBlockApi {
-
- private static final String ACT_BLOCK_PARAM = ACT_PREFIX + "getblockreward";
-
- private static final String BLOCKNO_PARAM = "&blockno=";
-
- BlockApiProvider(final IQueueManager queueManager,
- final String baseUrl,
- final IHttpExecutor executor) {
- super(queueManager, "block", baseUrl, executor);
- }
-
- @NotNull
- @Override
- public Optional uncles(long blockNumber) throws ApiException {
- final String urlParam = ACT_BLOCK_PARAM + BLOCKNO_PARAM + blockNumber;
- final String response = getRequest(urlParam);
- if (BasicUtils.isEmpty(response) || response.contains("NOTOK"))
- return Optional.empty();
-
- final UncleBlockResponseTO responseTO = convert(response, UncleBlockResponseTO.class);
- BasicUtils.validateTxResponse(responseTO);
- return (responseTO.getResult() == null || responseTO.getResult().isEmpty())
- ? Optional.empty()
- : Optional.of(responseTO.getResult());
- }
-}
diff --git a/src/main/java/io/api/etherscan/core/impl/ContractApiProvider.java b/src/main/java/io/api/etherscan/core/impl/ContractApiProvider.java
deleted file mode 100644
index 125087f..0000000
--- a/src/main/java/io/api/etherscan/core/impl/ContractApiProvider.java
+++ /dev/null
@@ -1,47 +0,0 @@
-package io.api.etherscan.core.impl;
-
-import io.api.etherscan.core.IContractApi;
-import io.api.etherscan.error.ApiException;
-import io.api.etherscan.error.EtherScanException;
-import io.api.etherscan.executor.IHttpExecutor;
-import io.api.etherscan.manager.IQueueManager;
-import io.api.etherscan.model.Abi;
-import io.api.etherscan.model.utility.StringResponseTO;
-import io.api.etherscan.util.BasicUtils;
-import org.jetbrains.annotations.NotNull;
-
-/**
- * Contract API Implementation
- *
- * @see IContractApi
- *
- * @author GoodforGod
- * @since 28.10.2018
- */
-public class ContractApiProvider extends BasicProvider implements IContractApi {
-
- private static final String ACT_ABI_PARAM = ACT_PREFIX + "getabi";
-
- private static final String ADDRESS_PARAM = "&address=";
-
- ContractApiProvider(final IQueueManager queueManager,
- final String baseUrl,
- final IHttpExecutor executor) {
- super(queueManager, "contract", baseUrl, executor);
- }
-
- @NotNull
- @Override
- public Abi contractAbi(final String address) throws ApiException {
- BasicUtils.validateAddress(address);
-
- final String urlParam = ACT_ABI_PARAM + ADDRESS_PARAM + address;
- final StringResponseTO response = getRequest(urlParam, StringResponseTO.class);
- if (response.getStatus() != 1 && "NOTOK".equals(response.getMessage()))
- throw new EtherScanException(response);
-
- return (response.getResult().startsWith("Contract sou"))
- ? Abi.nonVerified()
- : Abi.verified(response.getResult());
- }
-}
diff --git a/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java b/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java
deleted file mode 100644
index ba5dd83..0000000
--- a/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java
+++ /dev/null
@@ -1,140 +0,0 @@
-package io.api.etherscan.core.impl;
-
-import io.api.etherscan.core.*;
-import io.api.etherscan.error.ApiException;
-import io.api.etherscan.error.ApiKeyException;
-import io.api.etherscan.executor.IHttpExecutor;
-import io.api.etherscan.executor.impl.HttpExecutor;
-import io.api.etherscan.manager.IQueueManager;
-import io.api.etherscan.manager.impl.FakeQueueManager;
-import io.api.etherscan.manager.impl.QueueManager;
-import io.api.etherscan.model.EthNetwork;
-import io.api.etherscan.util.BasicUtils;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.function.Supplier;
-
-/**
- * EtherScan full API Description https://etherscan.io/apis
- *
- * @author GoodforGod
- * @since 28.10.2018
- */
-public class EtherScanApi implements AutoCloseable {
-
- private static final Supplier DEFAULT_SUPPLIER = HttpExecutor::new;
-
- public static final String DEFAULT_KEY = "YourApiKeyToken";
-
- private final IQueueManager queueManager;
- private final IAccountApi account;
- private final IBlockApi block;
- private final IContractApi contract;
- private final ILogsApi logs;
- private final IProxyApi proxy;
- private final IStatisticApi stats;
- private final ITransactionApi txs;
-
- public EtherScanApi() {
- this(DEFAULT_KEY, EthNetwork.MAINNET);
- }
-
- public EtherScanApi(final EthNetwork network) {
- this(DEFAULT_KEY, network);
- }
-
- public EtherScanApi(final String apiKey) {
- this(apiKey, EthNetwork.MAINNET);
- }
-
- public EtherScanApi(final EthNetwork network,
- final Supplier executorSupplier) {
- this(DEFAULT_KEY, network, executorSupplier);
- }
-
- public EtherScanApi(final String apiKey,
- final EthNetwork network,
- final IQueueManager queue) {
- this(apiKey, network, DEFAULT_SUPPLIER, queue);
- }
-
- public EtherScanApi(final String apiKey,
- final EthNetwork network) {
- this(apiKey, network, DEFAULT_SUPPLIER);
- }
-
- public EtherScanApi(final String apiKey,
- final EthNetwork network,
- final Supplier executorSupplier) {
- this(apiKey, network, executorSupplier,
- DEFAULT_KEY.equals(apiKey)
- ? QueueManager.DEFAULT_KEY_QUEUE
- : new FakeQueueManager());
- }
-
- public EtherScanApi(final String apiKey,
- final EthNetwork network,
- final Supplier executorSupplier,
- final IQueueManager queue) {
- if (BasicUtils.isBlank(apiKey))
- throw new ApiKeyException("API key can not be null or empty");
-
- if (network == null)
- throw new ApiException("Ethereum Network is set to NULL value");
-
- // EtherScan 1request\5sec limit support by queue manager
- final IHttpExecutor executor = executorSupplier.get();
-
- final String ending = EthNetwork.TOBALABA.equals(network) ? "com" : "io";
- final String baseUrl = "https://" + network.getDomain() + ".etherscan." + ending + "/api" + "?apikey=" + apiKey;
-
- this.queueManager = queue;
- this.account = new AccountApiProvider(queue, baseUrl, executor);
- this.block = new BlockApiProvider(queue, baseUrl, executor);
- this.contract = new ContractApiProvider(queue, baseUrl, executor);
- this.logs = new LogsApiProvider(queue, baseUrl, executor);
- this.proxy = new ProxyApiProvider(queue, baseUrl, executor);
- this.stats = new StatisticApiProvider(queue, baseUrl, executor);
- this.txs = new TransactionApiProvider(queue, baseUrl, executor);
- }
-
- @NotNull
- public IAccountApi account() {
- return account;
- }
-
- @NotNull
- public IContractApi contract() {
- return contract;
- }
-
- @NotNull
- public ITransactionApi txs() {
- return txs;
- }
-
- @NotNull
- public IBlockApi block() {
- return block;
- }
-
- @NotNull
- public ILogsApi logs() {
- return logs;
- }
-
- @NotNull
- public IProxyApi proxy() {
- return proxy;
- }
-
- @NotNull
- public IStatisticApi stats() {
- return stats;
- }
-
- @Override
- public void close() throws Exception {
- queueManager.close();
- }
-}
diff --git a/src/main/java/io/api/etherscan/core/impl/LogsApiProvider.java b/src/main/java/io/api/etherscan/core/impl/LogsApiProvider.java
deleted file mode 100644
index 6086869..0000000
--- a/src/main/java/io/api/etherscan/core/impl/LogsApiProvider.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package io.api.etherscan.core.impl;
-
-import io.api.etherscan.core.ILogsApi;
-import io.api.etherscan.error.ApiException;
-import io.api.etherscan.executor.IHttpExecutor;
-import io.api.etherscan.manager.IQueueManager;
-import io.api.etherscan.model.Log;
-import io.api.etherscan.model.query.impl.LogQuery;
-import io.api.etherscan.model.utility.LogResponseTO;
-import io.api.etherscan.util.BasicUtils;
-import org.jetbrains.annotations.NotNull;
-
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Logs API Implementation
- *
- * @see ILogsApi
- *
- * @author GoodforGod
- * @since 28.10.2018
- */
-public class LogsApiProvider extends BasicProvider implements ILogsApi {
-
- private static final String ACT_LOGS_PARAM = ACT_PREFIX + "getLogs";
-
- LogsApiProvider(final IQueueManager queue,
- final String baseUrl,
- final IHttpExecutor executor) {
- super(queue, "logs", baseUrl, executor);
- }
-
- @NotNull
- @Override
- public List logs(final LogQuery query) throws ApiException {
- final String urlParams = ACT_LOGS_PARAM + query.getParams();
- final LogResponseTO response = getRequest(urlParams, LogResponseTO.class);
- BasicUtils.validateTxResponse(response);
-
- return (BasicUtils.isEmpty(response.getResult()))
- ? Collections.emptyList()
- : response.getResult();
- }
-}
diff --git a/src/main/java/io/api/etherscan/core/impl/StatisticApiProvider.java b/src/main/java/io/api/etherscan/core/impl/StatisticApiProvider.java
deleted file mode 100644
index d178a81..0000000
--- a/src/main/java/io/api/etherscan/core/impl/StatisticApiProvider.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package io.api.etherscan.core.impl;
-
-import io.api.etherscan.core.IStatisticApi;
-import io.api.etherscan.error.ApiException;
-import io.api.etherscan.error.EtherScanException;
-import io.api.etherscan.executor.IHttpExecutor;
-import io.api.etherscan.manager.IQueueManager;
-import io.api.etherscan.model.Price;
-import io.api.etherscan.model.Supply;
-import io.api.etherscan.model.utility.PriceResponseTO;
-import io.api.etherscan.model.utility.StringResponseTO;
-import io.api.etherscan.util.BasicUtils;
-import org.jetbrains.annotations.NotNull;
-
-import java.math.BigInteger;
-
-/**
- * Statistic API Implementation
- *
- * @see IStatisticApi
- *
- * @author GoodforGod
- * @since 28.10.2018
- */
-public class StatisticApiProvider extends BasicProvider implements IStatisticApi {
-
- private static final String ACT_SUPPLY_PARAM = ACT_PREFIX + "ethsupply";
- private static final String ACT_TOKEN_SUPPLY_PARAM = ACT_PREFIX + "tokensupply";
- private static final String ACT_LASTPRICE_PARAM = ACT_PREFIX + "ethprice";
-
- private static final String CONTRACT_ADDRESS_PARAM = "&contractaddress=";
-
- StatisticApiProvider(final IQueueManager queue,
- final String baseUrl,
- final IHttpExecutor executor) {
- super(queue, "stats", baseUrl, executor);
- }
-
- @NotNull
- @Override
- public Supply supply() throws ApiException {
- final StringResponseTO response = getRequest(ACT_SUPPLY_PARAM, StringResponseTO.class);
- if (response.getStatus() != 1)
- throw new EtherScanException(response);
-
- return new Supply(new BigInteger(response.getResult()));
- }
-
- @NotNull
- @Override
- public BigInteger supply(final String contract) throws ApiException {
- BasicUtils.validateAddress(contract);
-
- final String urlParams = ACT_TOKEN_SUPPLY_PARAM + CONTRACT_ADDRESS_PARAM + contract;
- final StringResponseTO response = getRequest(urlParams, StringResponseTO.class);
- if (response.getStatus() != 1)
- throw new EtherScanException(response);
-
- return new BigInteger(response.getResult());
- }
-
- @NotNull
- @Override
- public Price lastPrice() throws ApiException {
- final PriceResponseTO response = getRequest(ACT_LASTPRICE_PARAM, PriceResponseTO.class);
- if (response.getStatus() != 1)
- throw new EtherScanException(response);
-
- return response.getResult();
- }
-}
diff --git a/src/main/java/io/api/etherscan/error/ApiException.java b/src/main/java/io/api/etherscan/error/ApiException.java
deleted file mode 100644
index 33e4228..0000000
--- a/src/main/java/io/api/etherscan/error/ApiException.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package io.api.etherscan.error;
-
-/**
- * @author GoodforGod
- * @since 30.10.2018
- */
-public class ApiException extends RuntimeException {
-
- public ApiException(String message) {
- super(message);
- }
-
- public ApiException(String message, Throwable cause) {
- super(message, cause);
- }
-}
diff --git a/src/main/java/io/api/etherscan/error/ApiKeyException.java b/src/main/java/io/api/etherscan/error/ApiKeyException.java
deleted file mode 100644
index 4e22934..0000000
--- a/src/main/java/io/api/etherscan/error/ApiKeyException.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package io.api.etherscan.error;
-
-/**
- * @author GoodforGod
- * @since 05.11.2018
- */
-public class ApiKeyException extends ApiException {
-
- public ApiKeyException(String message) {
- super(message);
- }
-}
diff --git a/src/main/java/io/api/etherscan/error/ApiTimeoutException.java b/src/main/java/io/api/etherscan/error/ApiTimeoutException.java
deleted file mode 100644
index 39b6e93..0000000
--- a/src/main/java/io/api/etherscan/error/ApiTimeoutException.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package io.api.etherscan.error;
-
-/**
- * @author GoodforGod
- * @since 12.11.2018
- */
-public class ApiTimeoutException extends ApiException {
-
- public ApiTimeoutException(String message, Throwable cause) {
- super(message, cause);
- }
-}
diff --git a/src/main/java/io/api/etherscan/error/ConnectionException.java b/src/main/java/io/api/etherscan/error/ConnectionException.java
deleted file mode 100644
index 96a881c..0000000
--- a/src/main/java/io/api/etherscan/error/ConnectionException.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package io.api.etherscan.error;
-
-/**
- * @author GoodforGod
- * @since 29.10.2018
- */
-public class ConnectionException extends ApiException {
-
- public ConnectionException(String message) {
- super(message);
- }
-
- public ConnectionException(String message, Throwable cause) {
- super(message, cause);
- }
-}
diff --git a/src/main/java/io/api/etherscan/error/EtherScanException.java b/src/main/java/io/api/etherscan/error/EtherScanException.java
deleted file mode 100644
index cb7dd7f..0000000
--- a/src/main/java/io/api/etherscan/error/EtherScanException.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package io.api.etherscan.error;
-
-import io.api.etherscan.model.utility.BaseResponseTO;
-import io.api.etherscan.model.utility.StringResponseTO;
-
-/**
- * @author GoodforGod
- * @since 29.10.2018
- */
-public class EtherScanException extends ApiException {
-
- public EtherScanException(BaseResponseTO response) {
- this(response.getMessage() + ", with status: " + response.getStatus());
- }
-
- public EtherScanException(StringResponseTO response) {
- this(response.getResult() + ", with status: " + response.getStatus() + ", with message: " + response.getMessage());
- }
-
- public EtherScanException(String message) {
- super(message);
- }
-}
diff --git a/src/main/java/io/api/etherscan/error/EventModelException.java b/src/main/java/io/api/etherscan/error/EventModelException.java
deleted file mode 100644
index feb60be..0000000
--- a/src/main/java/io/api/etherscan/error/EventModelException.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package io.api.etherscan.error;
-
-public class EventModelException extends ApiException {
-
- public EventModelException(String message) {
- super(message);
- }
-
- public EventModelException(String message, Throwable cause) {
- super(message, cause);
- }
-}
diff --git a/src/main/java/io/api/etherscan/error/InvalidAddressException.java b/src/main/java/io/api/etherscan/error/InvalidAddressException.java
deleted file mode 100644
index 9a0c143..0000000
--- a/src/main/java/io/api/etherscan/error/InvalidAddressException.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package io.api.etherscan.error;
-
-/**
- * @author GoodforGod
- * @since 29.10.2018
- */
-public class InvalidAddressException extends ApiException {
-
- public InvalidAddressException(String message) {
- super(message);
- }
-}
diff --git a/src/main/java/io/api/etherscan/error/InvalidDataHexException.java b/src/main/java/io/api/etherscan/error/InvalidDataHexException.java
deleted file mode 100644
index dd12cb9..0000000
--- a/src/main/java/io/api/etherscan/error/InvalidDataHexException.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package io.api.etherscan.error;
-
-/**
- * @author GoodforGod
- * @since 02.11.2018
- */
-public class InvalidDataHexException extends ApiException {
-
- public InvalidDataHexException(String message) {
- super(message);
- }
-}
diff --git a/src/main/java/io/api/etherscan/error/InvalidTxHashException.java b/src/main/java/io/api/etherscan/error/InvalidTxHashException.java
deleted file mode 100644
index aba32c1..0000000
--- a/src/main/java/io/api/etherscan/error/InvalidTxHashException.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package io.api.etherscan.error;
-
-/**
- * @author GoodforGod
- * @since 02.11.2018
- */
-public class InvalidTxHashException extends ApiException {
-
- public InvalidTxHashException(String message) {
- super(message);
- }
-}
diff --git a/src/main/java/io/api/etherscan/error/LogQueryException.java b/src/main/java/io/api/etherscan/error/LogQueryException.java
deleted file mode 100644
index 504219f..0000000
--- a/src/main/java/io/api/etherscan/error/LogQueryException.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package io.api.etherscan.error;
-
-/**
- * @author GoodforGod
- * @since 31.10.2018
- */
-public class LogQueryException extends ApiException {
-
- public LogQueryException(String message) {
- super(message);
- }
-}
diff --git a/src/main/java/io/api/etherscan/error/RateLimitException.java b/src/main/java/io/api/etherscan/error/RateLimitException.java
deleted file mode 100644
index c29f54d..0000000
--- a/src/main/java/io/api/etherscan/error/RateLimitException.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package io.api.etherscan.error;
-
-/**
- * @author iSnow
- * @since 2020-10-06
- */
-public class RateLimitException extends ApiException {
-
- public RateLimitException(String message) {
- super(message);
- }
-}
diff --git a/src/main/java/io/api/etherscan/executor/IHttpExecutor.java b/src/main/java/io/api/etherscan/executor/IHttpExecutor.java
deleted file mode 100644
index 0c80282..0000000
--- a/src/main/java/io/api/etherscan/executor/IHttpExecutor.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package io.api.etherscan.executor;
-
-/**
- * Http Client interface
- *
- * @author GoodforGod
- * @since 31.10.2018
- */
-public interface IHttpExecutor {
-
- /**
- * Performs a Http GET request
- *
- * @param url as string
- * @return result as string
- */
- String get(String url);
-
- /**
- * Performs a Http POST request
- *
- * @param url as string
- * @param data to post
- * @return result as string
- */
- String post(String url, String data);
-}
diff --git a/src/main/java/io/api/etherscan/executor/impl/HttpExecutor.java b/src/main/java/io/api/etherscan/executor/impl/HttpExecutor.java
deleted file mode 100644
index 5ba39f2..0000000
--- a/src/main/java/io/api/etherscan/executor/impl/HttpExecutor.java
+++ /dev/null
@@ -1,161 +0,0 @@
-package io.api.etherscan.executor.impl;
-
-import io.api.etherscan.error.ApiTimeoutException;
-import io.api.etherscan.error.ConnectionException;
-import io.api.etherscan.executor.IHttpExecutor;
-import io.api.etherscan.util.BasicUtils;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-import java.net.SocketTimeoutException;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.zip.GZIPInputStream;
-import java.util.zip.InflaterInputStream;
-
-import static java.net.HttpURLConnection.*;
-
-/**
- * Http client implementation
- *
- * @author GoodforGod
- * @see IHttpExecutor
- * @since 28.10.2018
- */
-public class HttpExecutor implements IHttpExecutor {
-
- private static final Map DEFAULT_HEADERS = new HashMap<>();
-
- private static final int CONNECT_TIMEOUT = 8000;
- private static final int READ_TIMEOUT = 0;
-
- static {
- DEFAULT_HEADERS.put("Accept-Language", "en");
- DEFAULT_HEADERS.put("Accept-Encoding", "deflate, gzip");
- DEFAULT_HEADERS.put("User-Agent", "Chrome/68.0.3440.106");
- DEFAULT_HEADERS.put("Accept-Charset", "UTF-8");
- }
-
- private final Map headers;
- private final int connectTimeout;
- private final int readTimeout;
-
- public HttpExecutor() {
- this(CONNECT_TIMEOUT);
- }
-
- public HttpExecutor(final int connectTimeout) {
- this(connectTimeout, READ_TIMEOUT);
- }
-
- public HttpExecutor(final int connectTimeout, final int readTimeout) {
- this(connectTimeout, readTimeout, DEFAULT_HEADERS);
- }
-
- /**
- * @param connectTimeout custom connection establish timeout in millis
- * @param readTimeout custom read timeout in millis
- * @param headers custom HTTP headers
- */
- public HttpExecutor(final int connectTimeout,
- final int readTimeout,
- final Map headers) {
- this.connectTimeout = Math.max(connectTimeout, 0);
- this.readTimeout = Math.max(readTimeout, 0);
- this.headers = headers;
- }
-
- private HttpURLConnection buildConnection(String urlAsString, String method) throws IOException {
- final URL url = new URL(urlAsString);
- final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
- connection.setRequestMethod(method);
- connection.setConnectTimeout(connectTimeout);
- connection.setReadTimeout(readTimeout);
- headers.forEach(connection::setRequestProperty);
- return connection;
- }
-
- @Override
- public String get(final String urlAsString) {
- try {
- final HttpURLConnection connection = buildConnection(urlAsString, "GET");
- final int status = connection.getResponseCode();
- if (status == HTTP_MOVED_TEMP || status == HTTP_MOVED_PERM) {
- return get(connection.getHeaderField("Location"));
- } else if ((status >= HTTP_BAD_REQUEST) && (status < HTTP_INTERNAL_ERROR)) {
- throw new ConnectionException("Protocol error: " + connection.getResponseMessage());
- } else if (status >= HTTP_INTERNAL_ERROR) {
- throw new ConnectionException("Server error: " + connection.getResponseMessage());
- }
-
- final String data = readData(connection);
- connection.disconnect();
- return data;
- } catch (SocketTimeoutException e) {
- throw new ApiTimeoutException("Timeout: Could not establish connection for " + connectTimeout + " millis", e);
- } catch (Exception e) {
- throw new ConnectionException(e.getMessage(), e);
- }
- }
-
- @Override
- public String post(final String urlAsString, final String dataToPost) {
- try {
- final HttpURLConnection connection = buildConnection(urlAsString, "POST");
- final String contentLength = (BasicUtils.isBlank(dataToPost)) ? "0" : String.valueOf(dataToPost.length());
- connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
- connection.setRequestProperty("Content-Length", contentLength);
- connection.setFixedLengthStreamingMode(dataToPost.length());
-
- connection.setDoOutput(true);
- connection.connect();
- try (OutputStream os = connection.getOutputStream()) {
- os.write(dataToPost.getBytes(StandardCharsets.UTF_8));
- }
-
- final int status = connection.getResponseCode();
- if (status == HTTP_MOVED_TEMP || status == HTTP_MOVED_PERM) {
- return post(connection.getHeaderField("Location"), dataToPost);
- } else if ((status >= HTTP_BAD_REQUEST) && (status < HTTP_INTERNAL_ERROR)) {
- throw new ConnectionException("Protocol error: " + connection.getResponseMessage());
- } else if (status >= HTTP_INTERNAL_ERROR) {
- throw new ConnectionException("Server error: " + connection.getResponseMessage());
- }
-
- final String data = readData(connection);
- connection.disconnect();
- return data;
- } catch (SocketTimeoutException e) {
- throw new ApiTimeoutException("Timeout: Could not establish connection for " + connectTimeout + " millis", e);
- } catch (Exception e) {
- throw new ConnectionException(e.getMessage(), e);
- }
- }
-
- private String readData(final HttpURLConnection connection) throws IOException {
- final StringBuilder content = new StringBuilder();
- try (BufferedReader in = new BufferedReader(getStreamReader(connection))) {
- String inputLine;
- while ((inputLine = in.readLine()) != null)
- content.append(inputLine);
- }
-
- return content.toString();
- }
-
- private InputStreamReader getStreamReader(final HttpURLConnection connection) throws IOException {
- switch (String.valueOf(connection.getContentEncoding())) {
- case "gzip":
- return new InputStreamReader(new GZIPInputStream(connection.getInputStream()), StandardCharsets.UTF_8);
- case "deflate":
- return new InputStreamReader(new InflaterInputStream(connection.getInputStream()), StandardCharsets.UTF_8);
- default:
- return new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8);
- }
- }
-}
diff --git a/src/main/java/io/api/etherscan/manager/IQueueManager.java b/src/main/java/io/api/etherscan/manager/IQueueManager.java
deleted file mode 100644
index 98a3172..0000000
--- a/src/main/java/io/api/etherscan/manager/IQueueManager.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package io.api.etherscan.manager;
-
-/**
- * Queue manager to support API limits (EtherScan 5request\sec limit) Managers grants turn if the
- * limit is not exhausted And resets queue each set period
- *
- * @author GoodforGod
- * @since 30.10.2018
- */
-public interface IQueueManager extends AutoCloseable {
-
- /**
- * Waits in queue for chance to take turn
- */
- void takeTurn();
-}
diff --git a/src/main/java/io/api/etherscan/manager/impl/QueueManager.java b/src/main/java/io/api/etherscan/manager/impl/QueueManager.java
deleted file mode 100644
index 764f7d5..0000000
--- a/src/main/java/io/api/etherscan/manager/impl/QueueManager.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package io.api.etherscan.manager.impl;
-
-import io.api.etherscan.manager.IQueueManager;
-
-import java.util.concurrent.*;
-
-/**
- * Queue Semaphore implementation with size and reset time as params
- *
- * @see IQueueManager
- *
- * @author GoodforGod
- * @since 30.10.2018
- */
-public class QueueManager implements IQueueManager, AutoCloseable {
-
- public static final QueueManager DEFAULT_KEY_QUEUE = new QueueManager(1, 5200L, 5200L, 0);
- public static final QueueManager PERSONAL_KEY_QUEUE = new QueueManager(5, 1100L, 1100L, 5);
-
- private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
- private final Semaphore semaphore;
- private final long queueResetTimeInMillis;
-
- public QueueManager(int size, int resetInSec) {
- this(size, resetInSec, resetInSec);
- }
-
- public QueueManager(int size, int queueResetTimeInSec, int delayInSec) {
- this(size, queueResetTimeInSec, delayInSec, size);
- }
-
- public QueueManager(int size, int queueResetTimeInSec, int delayInSec, int initialSize) {
- this(size,
- (long) queueResetTimeInSec * 1000,
- (long) delayInSec * 1000,
- initialSize);
- }
-
- public QueueManager(int size, long queueResetTimeInMillis, long delayInMillis, int initialSize) {
- this.queueResetTimeInMillis = queueResetTimeInMillis;
- this.semaphore = new Semaphore(initialSize);
- this.executorService.scheduleAtFixedRate(releaseLocks(size), delayInMillis, queueResetTimeInMillis,
- TimeUnit.MILLISECONDS);
- }
-
- @SuppressWarnings("java:S899")
- @Override
- public void takeTurn() {
- try {
- semaphore.tryAcquire(queueResetTimeInMillis, TimeUnit.MILLISECONDS);
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- }
- }
-
- private Runnable releaseLocks(int toRelease) {
- return () -> semaphore.release(toRelease);
- }
-
- @Override
- public void close() {
- executorService.shutdown();
- }
-}
diff --git a/src/main/java/io/api/etherscan/model/Balance.java b/src/main/java/io/api/etherscan/model/Balance.java
deleted file mode 100644
index cbd8502..0000000
--- a/src/main/java/io/api/etherscan/model/Balance.java
+++ /dev/null
@@ -1,84 +0,0 @@
-package io.api.etherscan.model;
-
-import io.api.etherscan.model.utility.BalanceTO;
-
-import java.math.BigInteger;
-import java.util.Objects;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 28.10.2018
- */
-public class Balance {
-
- /** Balance in Wei */
- private final Wei balance;
- private final String address;
-
- public Balance(final String address,
- final BigInteger balance) {
- this.address = address;
- this.balance = new Wei(balance);
- }
-
- public static Balance of(BalanceTO balance) {
- return new Balance(balance.getAccount(), new BigInteger(balance.getBalance()));
- }
-
- //
- public String getAddress() {
- return address;
- }
-
- public BigInteger getWei() {
- return balance.getValue();
- }
-
- public BigInteger getKwei() {
- return balance.asKwei();
- }
-
- public BigInteger getMwei() {
- return balance.asMwei();
- }
-
- public BigInteger getGwei() {
- return balance.asGwei();
- }
-
- public BigInteger getEther() {
- return balance.asEther();
- }
- //
-
- @Override
- public boolean equals(Object o) {
- if (this == o)
- return true;
- if (o == null || getClass() != o.getClass())
- return false;
-
- Balance balance1 = (Balance) o;
-
- if (!balance.equals(balance1.balance))
- return false;
- return Objects.equals(address, balance1.address);
- }
-
- @Override
- public int hashCode() {
- int result = balance.hashCode();
- result = 31 * result + (address != null ? address.hashCode() : 0);
- return result;
- }
-
- @Override
- public String toString() {
- return "Balance{" +
- "address='" + address + '\'' +
- ", balance=" + balance +
- '}';
- }
-}
diff --git a/src/main/java/io/api/etherscan/model/BaseTx.java b/src/main/java/io/api/etherscan/model/BaseTx.java
deleted file mode 100644
index 6eba826..0000000
--- a/src/main/java/io/api/etherscan/model/BaseTx.java
+++ /dev/null
@@ -1,124 +0,0 @@
-package io.api.etherscan.model;
-
-import com.google.gson.annotations.Expose;
-import io.api.etherscan.util.BasicUtils;
-
-import java.math.BigInteger;
-import java.time.LocalDateTime;
-import java.time.ZoneOffset;
-import java.util.Objects;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 28.10.2018
- */
-abstract class BaseTx {
-
- private long blockNumber;
- private String timeStamp;
- @Expose(serialize = false, deserialize = false)
- private LocalDateTime _timeStamp;
- private String hash;
- private String from;
- private String to;
- private BigInteger value;
- private String contractAddress;
- private String input;
- private BigInteger gas;
- private BigInteger gasUsed;
-
- //
- public long getBlockNumber() {
- return blockNumber;
- }
-
- public LocalDateTime getTimeStamp() {
- if (_timeStamp == null && !BasicUtils.isEmpty(timeStamp))
- _timeStamp = LocalDateTime.ofEpochSecond(Long.parseLong(timeStamp), 0, ZoneOffset.UTC);
- return _timeStamp;
- }
-
- public String getHash() {
- return hash;
- }
-
- public String getFrom() {
- return from;
- }
-
- public String getTo() {
- return to;
- }
-
- public BigInteger getValue() {
- return value;
- }
-
- public String getContractAddress() {
- return contractAddress;
- }
-
- public String getInput() {
- return input;
- }
-
- public BigInteger getGas() {
- return gas;
- }
-
- public BigInteger getGasUsed() {
- return gasUsed;
- }
- //
-
- @Override
- public boolean equals(Object o) {
- if (this == o)
- return true;
- if (!(o instanceof BaseTx))
- return false;
-
- BaseTx baseTx = (BaseTx) o;
-
- if (blockNumber != baseTx.blockNumber)
- return false;
- if (!Objects.equals(timeStamp, baseTx.timeStamp))
- return false;
- if (!Objects.equals(hash, baseTx.hash))
- return false;
- if (!Objects.equals(from, baseTx.from))
- return false;
- if (!Objects.equals(to, baseTx.to))
- return false;
- return Objects.equals(value, baseTx.value);
- }
-
- @Override
- public int hashCode() {
- int result = (int) (blockNumber ^ (blockNumber >>> 32));
- result = 31 * result + (timeStamp != null ? timeStamp.hashCode() : 0);
- result = 31 * result + (hash != null ? hash.hashCode() : 0);
- result = 31 * result + (from != null ? from.hashCode() : 0);
- result = 31 * result + (to != null ? to.hashCode() : 0);
- result = 31 * result + (value != null ? value.hashCode() : 0);
- return result;
- }
-
- @Override
- public String toString() {
- return "BaseTx{" +
- "blockNumber=" + blockNumber +
- ", timeStamp='" + timeStamp + '\'' +
- ", hash='" + hash + '\'' +
- ", from='" + from + '\'' +
- ", to='" + to + '\'' +
- ", value=" + value +
- ", contractAddress='" + contractAddress + '\'' +
- ", input='" + input + '\'' +
- ", gas=" + gas +
- ", gasUsed=" + gasUsed +
- '}';
- }
-}
diff --git a/src/main/java/io/api/etherscan/model/Block.java b/src/main/java/io/api/etherscan/model/Block.java
deleted file mode 100644
index 8853956..0000000
--- a/src/main/java/io/api/etherscan/model/Block.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package io.api.etherscan.model;
-
-import com.google.gson.annotations.Expose;
-import io.api.etherscan.util.BasicUtils;
-
-import java.math.BigInteger;
-import java.time.LocalDateTime;
-import java.time.ZoneOffset;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 28.10.2018
- */
-public class Block {
-
- private long blockNumber;
- private BigInteger blockReward;
- private String timeStamp;
- @Expose(serialize = false, deserialize = false)
- private LocalDateTime _timeStamp;
-
- //
- public long getBlockNumber() {
- return blockNumber;
- }
-
- public LocalDateTime getTimeStamp() {
- if (_timeStamp == null && !BasicUtils.isEmpty(timeStamp))
- _timeStamp = LocalDateTime.ofEpochSecond(Long.parseLong(timeStamp), 0, ZoneOffset.UTC);
- return _timeStamp;
- }
-
- public BigInteger getBlockReward() {
- return blockReward;
- }
- //
-
- @Override
- public boolean equals(Object o) {
- if (this == o)
- return true;
- if (o == null || getClass() != o.getClass())
- return false;
-
- Block block = (Block) o;
-
- return blockNumber == block.blockNumber;
- }
-
- @Override
- public int hashCode() {
- return (int) (blockNumber ^ (blockNumber >>> 32));
- }
-
- @Override
- public String toString() {
- return "Block{" +
- "blockNumber=" + blockNumber +
- ", blockReward=" + blockReward +
- ", timeStamp='" + timeStamp + '\'' +
- ", _timeStamp=" + _timeStamp +
- '}';
- }
-}
diff --git a/src/main/java/io/api/etherscan/model/EthNetwork.java b/src/main/java/io/api/etherscan/model/EthNetwork.java
deleted file mode 100644
index f7b91de..0000000
--- a/src/main/java/io/api/etherscan/model/EthNetwork.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package io.api.etherscan.model;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 28.10.2018
- */
-public enum EthNetwork {
-
- MAINNET("api"),
- ROPSTEN("api-ropsten"),
- KOVAN("api-kovan"),
- TOBALABA("api-tobalaba"),
- GORLI("api-goerli"),
- RINKEBY("api-rinkeby");
-
- private final String domain;
-
- EthNetwork(String domain) {
- this.domain = domain;
- }
-
- public String getDomain() {
- return domain;
- }
-}
diff --git a/src/main/java/io/api/etherscan/model/Log.java b/src/main/java/io/api/etherscan/model/Log.java
deleted file mode 100644
index 67ce96f..0000000
--- a/src/main/java/io/api/etherscan/model/Log.java
+++ /dev/null
@@ -1,176 +0,0 @@
-package io.api.etherscan.model;
-
-import com.google.gson.annotations.Expose;
-import io.api.etherscan.util.BasicUtils;
-
-import java.math.BigInteger;
-import java.time.LocalDateTime;
-import java.time.ZoneOffset;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 31.10.2018
- */
-public class Log {
-
- private String blockNumber;
- @Expose(serialize = false, deserialize = false)
- private Long _blockNumber;
- private String address;
- private String transactionHash;
- private String transactionIndex;
- @Expose(serialize = false, deserialize = false)
- private Long _transactionIndex;
- private String timeStamp;
- @Expose(serialize = false, deserialize = false)
- private LocalDateTime _timeStamp;
- private String data;
- private String gasPrice;
- @Expose(serialize = false, deserialize = false)
- private BigInteger _gasPrice;
- private String gasUsed;
- @Expose(serialize = false, deserialize = false)
- private BigInteger _gasUsed;
- private List topics;
- private String logIndex;
- @Expose(serialize = false, deserialize = false)
- private Long _logIndex;
-
- //
- public Long getBlockNumber() {
- if (_blockNumber == null && !BasicUtils.isEmpty(blockNumber)) {
- _blockNumber = BasicUtils.parseHex(blockNumber).longValue();
- }
- return _blockNumber;
- }
-
- public String getAddress() {
- return address;
- }
-
- public String getTransactionHash() {
- return transactionHash;
- }
-
- public Long getTransactionIndex() {
- if (_transactionIndex == null && !BasicUtils.isEmpty(transactionIndex)) {
- _transactionIndex = BasicUtils.parseHex(transactionIndex).longValue();
- }
-
- return _transactionIndex;
- }
-
- public LocalDateTime getTimeStamp() {
- if (_timeStamp == null && !BasicUtils.isEmpty(timeStamp)) {
- long formatted = (timeStamp.charAt(0) == '0' && timeStamp.charAt(1) == 'x')
- ? BasicUtils.parseHex(timeStamp).longValue()
- : Long.parseLong(timeStamp);
- _timeStamp = LocalDateTime.ofEpochSecond(formatted, 0, ZoneOffset.UTC);
- }
- return _timeStamp;
- }
-
- /**
- * Return the "timeStamp" field of the event record as a long-int representing the milliseconds
- * since the Unix epoch (1970-01-01 00:00:00).
- *
- * @return milliseconds between Unix epoch and `timeStamp`. If field is empty or null, returns null
- */
- public Long getTimeStampAsMillis() {
- if (BasicUtils.isEmpty(timeStamp)) {
- return null;
- }
- long tsSecs = (timeStamp.charAt(0) == '0' && timeStamp.charAt(1) == 'x')
- ? BasicUtils.parseHex(timeStamp).longValue()
- : Long.parseLong(timeStamp);
- return tsSecs * 1000;
- }
-
- public String getData() {
- return data;
- }
-
- public BigInteger getGasPrice() {
- if (!BasicUtils.isEmpty(gasPrice)) {
- _gasPrice = BasicUtils.parseHex(gasPrice);
- }
-
- return _gasPrice;
- }
-
- public BigInteger getGasUsed() {
- if (!BasicUtils.isEmpty(gasUsed)) {
- _gasUsed = BasicUtils.parseHex(gasUsed);
- }
-
- return _gasUsed;
- }
-
- public List getTopics() {
- return topics;
- }
-
- public Long getLogIndex() {
- if (_logIndex == null && !BasicUtils.isEmpty(logIndex)) {
- _logIndex = BasicUtils.parseHex(logIndex).longValue();
- }
- return _logIndex;
- }
- //
-
- @Override
- public boolean equals(Object o) {
- if (this == o)
- return true;
- if (o == null || getClass() != o.getClass())
- return false;
-
- Log log = (Log) o;
-
- if (!Objects.equals(blockNumber, log.blockNumber))
- return false;
- if (!Objects.equals(address, log.address))
- return false;
- if (!Objects.equals(transactionHash, log.transactionHash))
- return false;
- if (!Objects.equals(timeStamp, log.timeStamp))
- return false;
- return Objects.equals(logIndex, log.logIndex);
- }
-
- @Override
- public int hashCode() {
- int result = blockNumber != null ? blockNumber.hashCode() : 0;
- result = 31 * result + (address != null ? address.hashCode() : 0);
- result = 31 * result + (transactionHash != null ? transactionHash.hashCode() : 0);
- result = 31 * result + (timeStamp != null ? timeStamp.hashCode() : 0);
- result = 31 * result + (logIndex != null ? logIndex.hashCode() : 0);
- return result;
- }
-
- @Override
- public String toString() {
- return "Log{" +
- "blockNumber='" + blockNumber + '\'' +
- ", _blockNumber=" + _blockNumber +
- ", address='" + address + '\'' +
- ", transactionHash='" + transactionHash + '\'' +
- ", transactionIndex='" + transactionIndex + '\'' +
- ", _transactionIndex=" + _transactionIndex +
- ", timeStamp='" + timeStamp + '\'' +
- ", _timeStamp=" + _timeStamp +
- ", data='" + data + '\'' +
- ", gasPrice='" + gasPrice + '\'' +
- ", _gasPrice=" + _gasPrice +
- ", gasUsed='" + gasUsed + '\'' +
- ", _gasUsed=" + _gasUsed +
- ", topics=" + topics +
- ", logIndex='" + logIndex + '\'' +
- ", _logIndex=" + _logIndex +
- '}';
- }
-}
diff --git a/src/main/java/io/api/etherscan/model/Price.java b/src/main/java/io/api/etherscan/model/Price.java
deleted file mode 100644
index 9bc7dc7..0000000
--- a/src/main/java/io/api/etherscan/model/Price.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package io.api.etherscan.model;
-
-import com.google.gson.annotations.Expose;
-
-import java.time.LocalDateTime;
-import java.time.ZoneOffset;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 30.10.2018
- */
-public class Price {
-
- private double ethusd;
- private double ethbtc;
- private String ethusd_timestamp;
- private String ethbtc_timestamp;
- @Expose(serialize = false, deserialize = false)
- private LocalDateTime _ethusd_timestamp;
- @Expose(serialize = false, deserialize = false)
- private LocalDateTime _ethbtc_timestamp;
-
- public double inUsd() {
- return ethusd;
- }
-
- public double inBtc() {
- return ethbtc;
- }
-
- public LocalDateTime usdTimestamp() {
- if (_ethusd_timestamp == null)
- _ethusd_timestamp = LocalDateTime.ofEpochSecond(Long.parseLong(ethusd_timestamp), 0, ZoneOffset.UTC);
- return _ethusd_timestamp;
- }
-
- public LocalDateTime btcTimestamp() {
- if (_ethbtc_timestamp == null)
- _ethbtc_timestamp = LocalDateTime.ofEpochSecond(Long.parseLong(ethbtc_timestamp), 0, ZoneOffset.UTC);
- return _ethbtc_timestamp;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o)
- return true;
- if (o == null || getClass() != o.getClass())
- return false;
-
- Price price = (Price) o;
-
- if (Double.compare(price.ethusd, ethusd) != 0)
- return false;
- if (Double.compare(price.ethbtc, ethbtc) != 0)
- return false;
- if (ethusd_timestamp != null ? !ethusd_timestamp.equals(price.ethusd_timestamp) : price.ethusd_timestamp != null)
- return false;
- return (ethbtc_timestamp != null ? !ethbtc_timestamp.equals(price.ethbtc_timestamp) : price.ethbtc_timestamp != null);
- }
-
- @Override
- public int hashCode() {
- int result;
- long temp;
- temp = Double.doubleToLongBits(ethusd);
- result = (int) (temp ^ (temp >>> 32));
- temp = Double.doubleToLongBits(ethbtc);
- result = 31 * result + (int) (temp ^ (temp >>> 32));
- result = 31 * result + (ethusd_timestamp != null ? ethusd_timestamp.hashCode() : 0);
- result = 31 * result + (ethbtc_timestamp != null ? ethbtc_timestamp.hashCode() : 0);
- return result;
- }
-
- @Override
- public String toString() {
- return "Price{" +
- "ethusd=" + ethusd +
- ", ethbtc=" + ethbtc +
- ", ethusd_timestamp='" + ethusd_timestamp + '\'' +
- ", ethbtc_timestamp='" + ethbtc_timestamp + '\'' +
- '}';
- }
-}
diff --git a/src/main/java/io/api/etherscan/model/Status.java b/src/main/java/io/api/etherscan/model/Status.java
deleted file mode 100644
index 9683bde..0000000
--- a/src/main/java/io/api/etherscan/model/Status.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package io.api.etherscan.model;
-
-import java.util.Objects;
-
-/**
- * Contract Execution Status
- *
- * @author GoodforGod
- * @since 30.10.2018
- */
-public class Status {
-
- /**
- * "0" = Pass , isError":"1" = Error during Contract Execution
- */
- private int isError;
- private String errDescription;
-
- public boolean haveError() {
- return isError == 1;
- }
-
- public String getErrDescription() {
- return errDescription;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o)
- return true;
- if (o == null || getClass() != o.getClass())
- return false;
-
- Status status = (Status) o;
-
- if (isError != status.isError)
- return false;
- return Objects.equals(errDescription, status.errDescription);
- }
-
- @Override
- public int hashCode() {
- int result = isError;
- result = 31 * result + (errDescription != null ? errDescription.hashCode() : 0);
- return result;
- }
-
- @Override
- public String toString() {
- return "Status{" +
- "isError=" + isError +
- ", errDescription='" + errDescription + '\'' +
- '}';
- }
-}
diff --git a/src/main/java/io/api/etherscan/model/Supply.java b/src/main/java/io/api/etherscan/model/Supply.java
deleted file mode 100644
index 2fd6db7..0000000
--- a/src/main/java/io/api/etherscan/model/Supply.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package io.api.etherscan.model;
-
-import java.math.BigInteger;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 30.10.2018
- */
-public class Supply extends Wei {
-
- public Supply(BigInteger value) {
- super(value);
- }
-}
diff --git a/src/main/java/io/api/etherscan/model/Tx.java b/src/main/java/io/api/etherscan/model/Tx.java
deleted file mode 100644
index 4136d23..0000000
--- a/src/main/java/io/api/etherscan/model/Tx.java
+++ /dev/null
@@ -1,102 +0,0 @@
-package io.api.etherscan.model;
-
-import io.api.etherscan.util.BasicUtils;
-
-import java.math.BigInteger;
-import java.util.Objects;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 28.10.2018
- */
-public class Tx extends BaseTx {
-
- private long nonce;
- private String blockHash;
- private int transactionIndex;
- private BigInteger gasPrice;
- private BigInteger cumulativeGasUsed;
- private long confirmations;
- private String isError;
- private String txreceipt_status;
-
- //
- public long getNonce() {
- return nonce;
- }
-
- public String getBlockHash() {
- return blockHash;
- }
-
- public int getTransactionIndex() {
- return transactionIndex;
- }
-
- public BigInteger getGasPrice() {
- return gasPrice;
- }
-
- public boolean haveError() {
- return !BasicUtils.isEmpty(isError) && !isError.equals("0");
- }
-
- public String getTxreceipt_status() {
- return txreceipt_status;
- }
-
- public BigInteger getCumulativeGasUsed() {
- return cumulativeGasUsed;
- }
-
- public long getConfirmations() {
- return confirmations;
- }
- //
-
- @Override
- public boolean equals(Object o) {
- if (this == o)
- return true;
- if (o == null || getClass() != o.getClass())
- return false;
- if (!super.equals(o))
- return false;
-
- Tx tx = (Tx) o;
-
- if (nonce != tx.nonce)
- return false;
- if (transactionIndex != tx.transactionIndex)
- return false;
- if (!Objects.equals(blockHash, tx.blockHash))
- return false;
- return Objects.equals(isError, tx.isError);
- }
-
- @Override
- public int hashCode() {
- int result = super.hashCode();
- result = 31 * result + (int) (nonce ^ (nonce >>> 32));
- result = 31 * result + (blockHash != null ? blockHash.hashCode() : 0);
- result = 31 * result + transactionIndex;
- result = 31 * result + (isError != null ? isError.hashCode() : 0);
- return result;
- }
-
- @Override
- public String toString() {
- return "Tx{" +
- "nonce=" + nonce +
- ", blockHash='" + blockHash + '\'' +
- ", transactionIndex=" + transactionIndex +
- ", gasPrice=" + gasPrice +
- ", cumulativeGasUsed=" + cumulativeGasUsed +
- ", confirmations=" + confirmations +
- ", isError='" + isError + '\'' +
- ", txreceipt_status='" + txreceipt_status + '\'' +
- "} " + super.toString();
- }
-}
diff --git a/src/main/java/io/api/etherscan/model/TxInternal.java b/src/main/java/io/api/etherscan/model/TxInternal.java
deleted file mode 100644
index 5048947..0000000
--- a/src/main/java/io/api/etherscan/model/TxInternal.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package io.api.etherscan.model;
-
-import java.util.Objects;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 29.10.2018
- */
-public class TxInternal extends BaseTx {
-
- private String type;
- private String traceId;
- private int isError;
- private String errCode;
-
- //
- public String getType() {
- return type;
- }
-
- public long getTraceId() {
- return (traceId == null) ? 0 : Long.parseLong(traceId);
- }
-
- public String getTraceIdAsString() {
- return traceId;
- }
-
- public boolean haveError() {
- return isError == 1;
- }
-
- public String getErrCode() {
- return errCode;
- }
- //
-
- @Override
- public boolean equals(Object o) {
- if (this == o)
- return true;
- if (!(o instanceof TxInternal))
- return false;
- if (!super.equals(o))
- return false;
-
- TxInternal that = (TxInternal) o;
-
- if (!Objects.equals(traceId, that.traceId))
- return false;
- return Objects.equals(errCode, that.errCode);
- }
-
- @Override
- public int hashCode() {
- int result = super.hashCode();
- result = 31 * result + (traceId != null ? traceId.hashCode() : 0);
- result = 31 * result + (errCode != null ? errCode.hashCode() : 0);
- return result;
- }
-
- @Override
- public String toString() {
- return "TxInternal{" +
- "type='" + type + '\'' +
- ", traceId=" + traceId +
- ", isError=" + isError +
- ", errCode='" + errCode + '\'' +
- "} " + super.toString();
- }
-}
diff --git a/src/main/java/io/api/etherscan/model/TxToken.java b/src/main/java/io/api/etherscan/model/TxToken.java
deleted file mode 100644
index 8f5e36f..0000000
--- a/src/main/java/io/api/etherscan/model/TxToken.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package io.api.etherscan.model;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 28.10.2018
- */
-public class TxToken extends BaseTx {
-
- private long nonce;
- private String blockHash;
- private String tokenName;
- private String tokenSymbol;
- private String tokenDecimal;
- private int transactionIndex;
- private long gasPrice;
- private long cumulativeGasUsed;
- private long confirmations;
-
- //
- public long getNonce() {
- return nonce;
- }
-
- public String getBlockHash() {
- return blockHash;
- }
-
- public String getTokenName() {
- return tokenName;
- }
-
- public String getTokenSymbol() {
- return tokenSymbol;
- }
-
- public String getTokenDecimal() {
- return tokenDecimal;
- }
-
- public int getTransactionIndex() {
- return transactionIndex;
- }
-
- public long getGasPrice() {
- return gasPrice;
- }
-
- public long getCumulativeGasUsed() {
- return cumulativeGasUsed;
- }
-
- public long getConfirmations() {
- return confirmations;
- }
- //
-
- @Override
- public String toString() {
- return "TxToken{" +
- "nonce=" + nonce +
- ", blockHash='" + blockHash + '\'' +
- ", tokenName='" + tokenName + '\'' +
- ", tokenSymbol='" + tokenSymbol + '\'' +
- ", tokenDecimal='" + tokenDecimal + '\'' +
- ", transactionIndex=" + transactionIndex +
- ", gasPrice=" + gasPrice +
- ", cumulativeGasUsed=" + cumulativeGasUsed +
- ", confirmations=" + confirmations +
- "} " + super.toString();
- }
-}
diff --git a/src/main/java/io/api/etherscan/model/Uncle.java b/src/main/java/io/api/etherscan/model/Uncle.java
deleted file mode 100644
index 2ee206b..0000000
--- a/src/main/java/io/api/etherscan/model/Uncle.java
+++ /dev/null
@@ -1,63 +0,0 @@
-package io.api.etherscan.model;
-
-import java.math.BigInteger;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 30.10.2018
- */
-public class Uncle {
-
- private String miner;
- private BigInteger blockreward;
- private int unclePosition;
-
- //
- public String getMiner() {
- return miner;
- }
-
- public BigInteger getBlockreward() {
- return blockreward;
- }
-
- public int getUnclePosition() {
- return unclePosition;
- }
- //
-
- @Override
- public boolean equals(Object o) {
- if (this == o)
- return true;
- if (o == null || getClass() != o.getClass())
- return false;
-
- Uncle uncle = (Uncle) o;
-
- if (unclePosition != uncle.unclePosition)
- return false;
- if (miner != null ? !miner.equals(uncle.miner) : uncle.miner != null)
- return false;
- return blockreward != null ? blockreward.equals(uncle.blockreward) : uncle.blockreward == null;
- }
-
- @Override
- public int hashCode() {
- int result = miner != null ? miner.hashCode() : 0;
- result = 31 * result + (blockreward != null ? blockreward.hashCode() : 0);
- result = 31 * result + unclePosition;
- return result;
- }
-
- @Override
- public String toString() {
- return "Uncle{" +
- "miner='" + miner + '\'' +
- ", blockreward=" + blockreward +
- ", unclePosition=" + unclePosition +
- '}';
- }
-}
diff --git a/src/main/java/io/api/etherscan/model/UncleBlock.java b/src/main/java/io/api/etherscan/model/UncleBlock.java
deleted file mode 100644
index 88c975d..0000000
--- a/src/main/java/io/api/etherscan/model/UncleBlock.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package io.api.etherscan.model;
-
-import io.api.etherscan.util.BasicUtils;
-
-import java.util.List;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 30.10.2018
- */
-public class UncleBlock extends Block {
-
- private String blockMiner;
- private List uncles;
- private String uncleInclusionReward;
-
- //
- public boolean isEmpty() {
- return getBlockNumber() == 0 && getBlockReward() == null
- && getTimeStamp() == null
- && BasicUtils.isEmpty(blockMiner);
- }
-
- public String getBlockMiner() {
- return blockMiner;
- }
-
- public List getUncles() {
- return uncles;
- }
-
- public String getUncleInclusionReward() {
- return uncleInclusionReward;
- }
- //
-
- @Override
- public boolean equals(Object o) {
- if (this == o)
- return true;
- if (o == null || getClass() != o.getClass())
- return false;
- if (!super.equals(o))
- return false;
-
- UncleBlock that = (UncleBlock) o;
-
- return getBlockNumber() != 0 && getBlockNumber() == that.getBlockNumber();
- }
-
- @Override
- public int hashCode() {
- int result = super.hashCode();
- result = (int) (31 * result + getBlockNumber());
- return result;
- }
-
- @Override
- public String toString() {
- return "UncleBlock{" +
- "blockMiner='" + blockMiner + '\'' +
- ", uncles=" + uncles +
- ", uncleInclusionReward='" + uncleInclusionReward + '\'' +
- '}';
- }
-}
diff --git a/src/main/java/io/api/etherscan/model/Wei.java b/src/main/java/io/api/etherscan/model/Wei.java
deleted file mode 100644
index eddf8d2..0000000
--- a/src/main/java/io/api/etherscan/model/Wei.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package io.api.etherscan.model;
-
-import java.math.BigInteger;
-import java.util.Objects;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 30.10.2018
- */
-public class Wei {
-
- private BigInteger result;
-
- public Wei(BigInteger value) {
- this.result = value;
- }
-
- //
- public BigInteger getValue() {
- return result;
- }
-
- public BigInteger asKwei() {
- return result.divide(BigInteger.valueOf(1000));
- }
-
- public BigInteger asMwei() {
- return result.divide(BigInteger.valueOf(1000000));
- }
-
- public BigInteger asGwei() {
- return result.divide(BigInteger.valueOf(1000000000));
- }
-
- public BigInteger asEther() {
- return result.divide(BigInteger.valueOf(1000000000000000L));
- }
- //
-
- @Override
- public boolean equals(Object o) {
- if (this == o)
- return true;
- if (o == null || getClass() != o.getClass())
- return false;
-
- Wei wei = (Wei) o;
- return Objects.equals(result, wei.result);
- }
-
- @Override
- public int hashCode() {
- return result != null ? result.hashCode() : 0;
- }
-
- @Override
- public String toString() {
- return "Wei{" +
- "result=" + result +
- '}';
- }
-}
diff --git a/src/main/java/io/api/etherscan/model/proxy/BlockProxy.java b/src/main/java/io/api/etherscan/model/proxy/BlockProxy.java
deleted file mode 100644
index 63821c0..0000000
--- a/src/main/java/io/api/etherscan/model/proxy/BlockProxy.java
+++ /dev/null
@@ -1,199 +0,0 @@
-package io.api.etherscan.model.proxy;
-
-import com.google.gson.annotations.Expose;
-import io.api.etherscan.util.BasicUtils;
-
-import java.math.BigInteger;
-import java.time.LocalDateTime;
-import java.time.ZoneOffset;
-import java.util.List;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 31.10.2018
- */
-public class BlockProxy {
-
- private String number;
- @Expose(serialize = false, deserialize = false)
- private Long _number;
- private String hash;
- private String parentHash;
- private String stateRoot;
- private String size;
- @Expose(serialize = false, deserialize = false)
- private Long _size;
- private String difficulty;
- private String totalDifficulty;
- private String timestamp;
- @Expose(serialize = false, deserialize = false)
- private LocalDateTime _timestamp;
-
- private String miner;
- private String nonce;
- private String extraData;
- private String logsBloom;
- private String mixHash;
- private String gasUsed;
- @Expose(serialize = false, deserialize = false)
- private BigInteger _gasUsed;
- private String gasLimit;
- @Expose(serialize = false, deserialize = false)
- private BigInteger _gasLimit;
-
- private String sha3Uncles;
- private List uncles;
-
- private String receiptsRoot;
- private String transactionsRoot;
- private List transactions;
-
- //
- public Long getNumber() {
- if (_number == null && !BasicUtils.isEmpty(number))
- _number = BasicUtils.parseHex(number).longValue();
- return _number;
- }
-
- public String getHash() {
- return hash;
- }
-
- public String getParentHash() {
- return parentHash;
- }
-
- public String getStateRoot() {
- return stateRoot;
- }
-
- public Long getSize() {
- if (_size == null && !BasicUtils.isEmpty(size))
- _size = BasicUtils.parseHex(size).longValue();
- return _size;
- }
-
- public String getDifficulty() {
- return difficulty;
- }
-
- public String getTotalDifficulty() {
- return totalDifficulty;
- }
-
- public LocalDateTime getTimeStamp() {
- if (_timestamp == null && !BasicUtils.isEmpty(timestamp))
- _timestamp = LocalDateTime.ofEpochSecond(BasicUtils.parseHex(timestamp).longValue(), 0, ZoneOffset.UTC);
- return _timestamp;
- }
-
- public String getMiner() {
- return miner;
- }
-
- public String getNonce() {
- return nonce;
- }
-
- public String getExtraData() {
- return extraData;
- }
-
- public String getLogsBloom() {
- return logsBloom;
- }
-
- public String getMixHash() {
- return mixHash;
- }
-
- public BigInteger getGasUsed() {
- if (_gasUsed == null && !BasicUtils.isEmpty(gasUsed))
- _gasUsed = BasicUtils.parseHex(gasUsed);
- return _gasUsed;
- }
-
- public BigInteger getGasLimit() {
- if (_gasLimit == null && !BasicUtils.isEmpty(gasLimit))
- _gasLimit = BasicUtils.parseHex(gasLimit);
- return _gasLimit;
- }
-
- public String getSha3Uncles() {
- return sha3Uncles;
- }
-
- public List getUncles() {
- return uncles;
- }
-
- public String getReceiptsRoot() {
- return receiptsRoot;
- }
-
- public String getTransactionsRoot() {
- return transactionsRoot;
- }
-
- public List getTransactions() {
- return transactions;
- }
- //
-
- @Override
- public boolean equals(Object o) {
- if (this == o)
- return true;
- if (o == null || getClass() != o.getClass())
- return false;
-
- BlockProxy that = (BlockProxy) o;
-
- if (number != null ? !number.equals(that.number) : that.number != null)
- return false;
- if (hash != null ? !hash.equals(that.hash) : that.hash != null)
- return false;
- return parentHash != null ? parentHash.equals(that.parentHash) : that.parentHash == null;
- }
-
- @Override
- public int hashCode() {
- int result = number != null ? number.hashCode() : 0;
- result = 31 * result + (hash != null ? hash.hashCode() : 0);
- result = 31 * result + (parentHash != null ? parentHash.hashCode() : 0);
- return result;
- }
-
- @Override
- public String toString() {
- return "BlockProxy{" +
- "number='" + number + '\'' +
- ", _number=" + _number +
- ", hash='" + hash + '\'' +
- ", parentHash='" + parentHash + '\'' +
- ", stateRoot='" + stateRoot + '\'' +
- ", size='" + size + '\'' +
- ", _size=" + _size +
- ", difficulty='" + difficulty + '\'' +
- ", totalDifficulty='" + totalDifficulty + '\'' +
- ", timestamp='" + timestamp + '\'' +
- ", _timestamp=" + _timestamp +
- ", miner='" + miner + '\'' +
- ", nonce='" + nonce + '\'' +
- ", extraData='" + extraData + '\'' +
- ", logsBloom='" + logsBloom + '\'' +
- ", mixHash='" + mixHash + '\'' +
- ", gasUsed='" + gasUsed + '\'' +
- ", _gasUsed=" + _gasUsed +
- ", gasLimit='" + gasLimit + '\'' +
- ", _gasLimit=" + _gasLimit +
- ", sha3Uncles='" + sha3Uncles + '\'' +
- ", uncles=" + uncles +
- ", receiptsRoot='" + receiptsRoot + '\'' +
- ", transactionsRoot='" + transactionsRoot + '\'' +
- ", transactions=" + transactions +
- '}';
- }
-}
diff --git a/src/main/java/io/api/etherscan/model/proxy/ReceiptProxy.java b/src/main/java/io/api/etherscan/model/proxy/ReceiptProxy.java
deleted file mode 100644
index f40cb59..0000000
--- a/src/main/java/io/api/etherscan/model/proxy/ReceiptProxy.java
+++ /dev/null
@@ -1,143 +0,0 @@
-package io.api.etherscan.model.proxy;
-
-import com.google.gson.annotations.Expose;
-import io.api.etherscan.model.Log;
-import io.api.etherscan.util.BasicUtils;
-
-import java.math.BigInteger;
-import java.util.List;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 03.11.2018
- */
-public class ReceiptProxy {
-
- private String root;
- private String from;
- private String to;
- private String blockNumber;
- @Expose(serialize = false, deserialize = false)
- private Long _blockNumber;
- private String blockHash;
- private String transactionHash;
- private String transactionIndex;
- @Expose(serialize = false, deserialize = false)
- private Long _transactionIndex;
- private String gasUsed;
- @Expose(serialize = false, deserialize = false)
- private BigInteger _gasUsed;
- private String cumulativeGasUsed;
- @Expose(serialize = false, deserialize = false)
- private BigInteger _cumulativeGasUsed;
- private String contractAddress;
-
- private List logs;
- private String logsBloom;
-
- //
- public String getRoot() {
- return root;
- }
-
- public String getFrom() {
- return from;
- }
-
- public String getTo() {
- return to;
- }
-
- public Long getBlockNumber() {
- if (_blockNumber == null && !BasicUtils.isEmpty(blockNumber))
- _blockNumber = BasicUtils.parseHex(blockNumber).longValue();
- return _blockNumber;
- }
-
- public String getBlockHash() {
- return blockHash;
- }
-
- public String getTransactionHash() {
- return transactionHash;
- }
-
- public Long getTransactionIndex() {
- if (_transactionIndex == null && !BasicUtils.isEmpty(transactionIndex))
- _transactionIndex = BasicUtils.parseHex(transactionIndex).longValue();
- return _transactionIndex;
- }
-
- public BigInteger getGasUsed() {
- if (_gasUsed == null && !BasicUtils.isEmpty(gasUsed))
- _gasUsed = BasicUtils.parseHex(gasUsed);
- return _gasUsed;
- }
-
- public BigInteger getCumulativeGasUsed() {
- if (_cumulativeGasUsed == null && !BasicUtils.isEmpty(cumulativeGasUsed))
- _cumulativeGasUsed = BasicUtils.parseHex(cumulativeGasUsed);
- return _cumulativeGasUsed;
- }
-
- public String getContractAddress() {
- return contractAddress;
- }
-
- public List getLogs() {
- return logs;
- }
-
- public String getLogsBloom() {
- return logsBloom;
- }
- //
-
- @Override
- public boolean equals(Object o) {
- if (this == o)
- return true;
- if (o == null || getClass() != o.getClass())
- return false;
-
- ReceiptProxy that = (ReceiptProxy) o;
-
- if (blockNumber != null ? !blockNumber.equals(that.blockNumber) : that.blockNumber != null)
- return false;
- if (transactionHash != null ? !transactionHash.equals(that.transactionHash) : that.transactionHash != null)
- return false;
- return transactionIndex != null ? transactionIndex.equals(that.transactionIndex) : that.transactionIndex == null;
- }
-
- @Override
- public int hashCode() {
- int result = blockNumber != null ? blockNumber.hashCode() : 0;
- result = 31 * result + (transactionHash != null ? transactionHash.hashCode() : 0);
- result = 31 * result + (transactionIndex != null ? transactionIndex.hashCode() : 0);
- return result;
- }
-
- @Override
- public String toString() {
- return "ReceiptProxy{" +
- "root='" + root + '\'' +
- ", from='" + from + '\'' +
- ", to='" + to + '\'' +
- ", blockNumber='" + blockNumber + '\'' +
- ", _blockNumber=" + _blockNumber +
- ", blockHash='" + blockHash + '\'' +
- ", transactionHash='" + transactionHash + '\'' +
- ", transactionIndex='" + transactionIndex + '\'' +
- ", _transactionIndex=" + _transactionIndex +
- ", gasUsed='" + gasUsed + '\'' +
- ", _gasUsed=" + _gasUsed +
- ", cumulativeGasUsed='" + cumulativeGasUsed + '\'' +
- ", _cumulativeGasUsed=" + _cumulativeGasUsed +
- ", contractAddress='" + contractAddress + '\'' +
- ", logs=" + logs +
- ", logsBloom='" + logsBloom + '\'' +
- '}';
- }
-}
diff --git a/src/main/java/io/api/etherscan/model/proxy/TxProxy.java b/src/main/java/io/api/etherscan/model/proxy/TxProxy.java
deleted file mode 100644
index 5c7b5c8..0000000
--- a/src/main/java/io/api/etherscan/model/proxy/TxProxy.java
+++ /dev/null
@@ -1,157 +0,0 @@
-package io.api.etherscan.model.proxy;
-
-import com.google.gson.annotations.Expose;
-import io.api.etherscan.util.BasicUtils;
-
-import java.math.BigInteger;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 31.10.2018
- */
-public class TxProxy {
-
- private String to;
- private String hash;
- private String transactionIndex;
- @Expose(serialize = false, deserialize = false)
- private Long _transactionIndex;
- private String from;
- private String v;
- private String input;
- private String s;
- private String r;
- private String nonce;
- @Expose(serialize = false, deserialize = false)
- private Long _nonce;
- private String value;
- private String gas;
- @Expose(serialize = false, deserialize = false)
- private BigInteger _gas;
- private String gasPrice;
- @Expose(serialize = false, deserialize = false)
- private BigInteger _gasPrice;
- private String blockHash;
- private String blockNumber;
- @Expose(serialize = false, deserialize = false)
- private Long _blockNumber;
-
- //
- public String getTo() {
- return to;
- }
-
- public String getHash() {
- return hash;
- }
-
- public Long getTransactionIndex() {
- if (_transactionIndex == null && !BasicUtils.isEmpty(transactionIndex))
- _transactionIndex = BasicUtils.parseHex(transactionIndex).longValue();
- return _transactionIndex;
- }
-
- public String getFrom() {
- return from;
- }
-
- public BigInteger getGas() {
- if (_gas == null && !BasicUtils.isEmpty(gas))
- _gas = BasicUtils.parseHex(gas);
- return _gas;
- }
-
- public String getV() {
- return v;
- }
-
- public String getInput() {
- return input;
- }
-
- public String getS() {
- return s;
- }
-
- public String getR() {
- return r;
- }
-
- public Long getNonce() {
- if (_nonce == null && !BasicUtils.isEmpty(nonce))
- _nonce = BasicUtils.parseHex(nonce).longValue();
- return _nonce;
- }
-
- public String getValue() {
- return value;
- }
-
- public BigInteger getGasPrice() {
- if (_gasPrice == null && !BasicUtils.isEmpty(gasPrice))
- _gasPrice = BasicUtils.parseHex(gasPrice);
- return _gasPrice;
- }
-
- public String getBlockHash() {
- return blockHash;
- }
-
- public Long getBlockNumber() {
- if (_blockNumber == null && !BasicUtils.isEmpty(blockNumber))
- _blockNumber = BasicUtils.parseHex(blockNumber).longValue();
- return _blockNumber;
- }
- //
-
- @Override
- public boolean equals(Object o) {
- if (this == o)
- return true;
- if (o == null || getClass() != o.getClass())
- return false;
-
- TxProxy txProxy = (TxProxy) o;
-
- if (hash != null ? !hash.equals(txProxy.hash) : txProxy.hash != null)
- return false;
- if (blockHash != null ? !blockHash.equals(txProxy.blockHash) : txProxy.blockHash != null)
- return false;
- return blockNumber != null ? blockNumber.equals(txProxy.blockNumber) : txProxy.blockNumber == null;
- }
-
- @Override
- public int hashCode() {
- int result = hash != null ? hash.hashCode() : 0;
- result = 31 * result + (blockHash != null ? blockHash.hashCode() : 0);
- result = 31 * result + (blockNumber != null ? blockNumber.hashCode() : 0);
- return result;
- }
-
- @Override
- public String toString() {
- return "TxProxy{" +
- "to='" + to + '\'' +
- ", hash='" + hash + '\'' +
- ", transactionIndex='" + transactionIndex + '\'' +
- ", _transactionIndex=" + _transactionIndex +
- ", from='" + from + '\'' +
- ", v='" + v + '\'' +
- ", input='" + input + '\'' +
- ", s='" + s + '\'' +
- ", r='" + r + '\'' +
- ", nonce='" + nonce + '\'' +
- ", _nonce=" + _nonce +
- ", value='" + value + '\'' +
- ", gas='" + gas + '\'' +
- ", _gas=" + _gas +
- ", gasPrice='" + gasPrice + '\'' +
- ", _gasPrice=" + _gasPrice +
- ", blockHash='" + blockHash + '\'' +
- ", blockNumber='" + blockNumber + '\'' +
- ", _blockNumber=" + _blockNumber +
- '}';
- }
-}
diff --git a/src/main/java/io/api/etherscan/model/query/IQueryBuilder.java b/src/main/java/io/api/etherscan/model/query/IQueryBuilder.java
deleted file mode 100644
index 6a76c62..0000000
--- a/src/main/java/io/api/etherscan/model/query/IQueryBuilder.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package io.api.etherscan.model.query;
-
-import io.api.etherscan.error.LogQueryException;
-import io.api.etherscan.model.query.impl.LogQuery;
-
-/**
- * Builder, part of The Event Log API
- *
- * @author GoodforGod
- * @since 31.10.2018
- */
-public interface IQueryBuilder {
-
- LogQuery build() throws LogQueryException;
-}
diff --git a/src/main/java/io/api/etherscan/model/query/impl/LogQuery.java b/src/main/java/io/api/etherscan/model/query/impl/LogQuery.java
deleted file mode 100644
index 3ba6c4f..0000000
--- a/src/main/java/io/api/etherscan/model/query/impl/LogQuery.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package io.api.etherscan.model.query.impl;
-
-import io.api.etherscan.core.ILogsApi;
-
-/**
- * Final builded container for The Event Log API
- *
- * EtherScan - API Descriptions https://etherscan.io/apis#logs
- *
- * @see LogQueryBuilder
- * @see ILogsApi
- *
- * @author GoodforGod
- * @since 31.10.2018
- */
-public class LogQuery {
-
- /**
- * Final request parameter for api call
- */
- private final String params;
-
- LogQuery(String params) {
- this.params = params;
- }
-
- public String getParams() {
- return params;
- }
-}
diff --git a/src/main/java/io/api/etherscan/model/query/impl/LogQueryBuilder.java b/src/main/java/io/api/etherscan/model/query/impl/LogQueryBuilder.java
deleted file mode 100644
index bd8a9fc..0000000
--- a/src/main/java/io/api/etherscan/model/query/impl/LogQueryBuilder.java
+++ /dev/null
@@ -1,84 +0,0 @@
-package io.api.etherscan.model.query.impl;
-
-import io.api.etherscan.core.ILogsApi;
-import io.api.etherscan.error.LogQueryException;
-import io.api.etherscan.model.query.IQueryBuilder;
-import io.api.etherscan.util.BasicUtils;
-
-/**
- * Builder for The Event Log API
- *
- * @see ILogsApi
- *
- * @author GoodforGod
- * @since 31.10.2018
- */
-public class LogQueryBuilder implements IQueryBuilder {
-
- private static final long MIN_BLOCK = 0;
- private static final long MAX_BLOCK = 99999999999L;
-
- private final String address;
- private final long startBlock, endBlock;
-
- private LogQueryBuilder(String address, long startBlock, long endBlock) {
- this.address = address;
- this.startBlock = startBlock;
- this.endBlock = endBlock;
- }
-
- public static LogQueryBuilder with(String address) {
- return with(address, MIN_BLOCK);
- }
-
- public static LogQueryBuilder with(String address, long startBlock) {
- return with(address, startBlock, MAX_BLOCK);
- }
-
- public static LogQueryBuilder with(String address, long startBlock, long endBlock) {
- BasicUtils.validateAddress(address);
- return new LogQueryBuilder(address, startBlock, endBlock);
- }
-
- public LogTopicSingle topic(String topic0) {
- if (BasicUtils.isNotHex(topic0))
- throw new LogQueryException("topic0 can not be empty or non hex.");
- return new LogTopicSingle(address, startBlock, endBlock, topic0);
- }
-
- public LogTopicTuple topic(String topic0, String topic1) {
- if (BasicUtils.isNotHex(topic0))
- throw new LogQueryException("topic0 can not be empty or non hex.");
- if (BasicUtils.isNotHex(topic1))
- throw new LogQueryException("topic1 can not be empty or non hex.");
- return new LogTopicTuple(address, startBlock, endBlock, topic0, topic1);
- }
-
- public LogTopicTriple topic(String topic0, String topic1, String topic2) {
- if (BasicUtils.isNotHex(topic0))
- throw new LogQueryException("topic0 can not be empty or non hex.");
- if (BasicUtils.isNotHex(topic1))
- throw new LogQueryException("topic1 can not be empty or non hex.");
- if (BasicUtils.isNotHex(topic2))
- throw new LogQueryException("topic2 can not be empty or non hex.");
- return new LogTopicTriple(address, startBlock, endBlock, topic0, topic1, topic2);
- }
-
- public LogTopicQuadro topic(String topic0, String topic1, String topic2, String topic3) {
- if (BasicUtils.isNotHex(topic0))
- throw new LogQueryException("topic0 can not be empty or non hex.");
- if (BasicUtils.isNotHex(topic1))
- throw new LogQueryException("topic1 can not be empty or non hex.");
- if (BasicUtils.isNotHex(topic2))
- throw new LogQueryException("topic2 can not be empty or non hex.");
- if (BasicUtils.isNotHex(topic3))
- throw new LogQueryException("topic3 can not be empty or non hex.");
-
- return new LogTopicQuadro(address, startBlock, endBlock, topic0, topic1, topic2, topic3);
- }
-
- @Override
- public LogQuery build() throws LogQueryException {
- return new LogQuery("&address=" + this.address + "&fromBlock=" + this.startBlock + "&toBlock=" + this.endBlock);
- }
-}
diff --git a/src/main/java/io/api/etherscan/model/utility/BaseResponseTO.java b/src/main/java/io/api/etherscan/model/utility/BaseResponseTO.java
deleted file mode 100644
index d3653e2..0000000
--- a/src/main/java/io/api/etherscan/model/utility/BaseResponseTO.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package io.api.etherscan.model.utility;
-
-import io.api.etherscan.util.BasicUtils;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 29.10.2018
- */
-public abstract class BaseResponseTO {
-
- private String status;
- private String message;
-
- public int getStatus() {
- return BasicUtils.isEmpty(status) ? -1 : Integer.parseInt(status);
- }
-
- public String getMessage() {
- return message;
- }
-}
diff --git a/src/main/java/io/api/etherscan/model/utility/StringResponseTO.java b/src/main/java/io/api/etherscan/model/utility/StringResponseTO.java
deleted file mode 100644
index 38d3c86..0000000
--- a/src/main/java/io/api/etherscan/model/utility/StringResponseTO.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package io.api.etherscan.model.utility;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 29.10.2018
- */
-public class StringResponseTO extends BaseResponseTO {
-
- private String result;
-
- public String getResult() {
- return result;
- }
-}
diff --git a/src/main/java/io/api/etherscan/model/utility/TxTokenResponseTO.java b/src/main/java/io/api/etherscan/model/utility/TxTokenResponseTO.java
deleted file mode 100644
index 5ac2aec..0000000
--- a/src/main/java/io/api/etherscan/model/utility/TxTokenResponseTO.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package io.api.etherscan.model.utility;
-
-import io.api.etherscan.model.TxToken;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 29.10.2018
- */
-public class TxTokenResponseTO extends BaseListResponseTO {
-
-}
diff --git a/src/main/java/io/api/etherscan/model/utility/UncleBlockResponseTO.java b/src/main/java/io/api/etherscan/model/utility/UncleBlockResponseTO.java
deleted file mode 100644
index f4f4349..0000000
--- a/src/main/java/io/api/etherscan/model/utility/UncleBlockResponseTO.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package io.api.etherscan.model.utility;
-
-import io.api.etherscan.model.UncleBlock;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 30.10.2018
- */
-public class UncleBlockResponseTO extends BaseResponseTO {
-
- private UncleBlock result;
-
- public UncleBlock getResult() {
- return result;
- }
-}
diff --git a/src/main/java/io/goodforgod/api/etherscan/AccountAPI.java b/src/main/java/io/goodforgod/api/etherscan/AccountAPI.java
new file mode 100644
index 0000000..09c49eb
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/AccountAPI.java
@@ -0,0 +1,216 @@
+package io.goodforgod.api.etherscan;
+
+import io.goodforgod.api.etherscan.error.EtherScanException;
+import io.goodforgod.api.etherscan.model.*;
+import java.util.List;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * EtherScan - API Descriptions ...
+ *
+ * @author GoodforGod
+ * @since 28.10.2018
+ */
+public interface AccountAPI {
+
+ /**
+ * Address ETH balance
+ *
+ * @param address get balance for
+ * @return balance
+ * @throws EtherScanException parent exception class
+ */
+ @NotNull
+ Balance balance(@NotNull String address) throws EtherScanException;
+
+ /**
+ * ERC20 token balance for address
+ *
+ * @param address get balance for
+ * @param contract token contract
+ * @return token balance for address
+ * @throws EtherScanException parent exception class
+ */
+ @NotNull
+ TokenBalance balance(@NotNull String address, @NotNull String contract) throws EtherScanException;
+
+ /**
+ * Maximum 20 address for single batch request If address MORE THAN 20, then there will be more than
+ * 1 request performed
+ *
+ * @param addresses addresses to get balances for
+ * @return list of balances
+ * @throws EtherScanException parent exception class
+ */
+ @NotNull
+ List balances(@NotNull List addresses) throws EtherScanException;
+
+ /**
+ * All txs for given address
+ *
+ * @param address get txs for
+ * @param startBlock tx from this blockNumber
+ * @param endBlock tx to this blockNumber
+ * @return txs for address
+ * @throws EtherScanException parent exception class
+ */
+ @NotNull
+ List txs(@NotNull String address, long startBlock, long endBlock) throws EtherScanException;
+
+ @NotNull
+ List txs(@NotNull String address, long startBlock) throws EtherScanException;
+
+ @NotNull
+ List txs(@NotNull String address) throws EtherScanException;
+
+ /**
+ * All internal txs for given address
+ *
+ * @param address get txs for
+ * @param startBlock tx from this blockNumber
+ * @param endBlock tx to this blockNumber
+ * @return txs for address
+ * @throws EtherScanException parent exception class
+ */
+ @NotNull
+ List txsInternal(@NotNull String address, long startBlock, long endBlock) throws EtherScanException;
+
+ @NotNull
+ List txsInternal(@NotNull String address, long startBlock) throws EtherScanException;
+
+ @NotNull
+ List txsInternal(@NotNull String address) throws EtherScanException;
+
+ /**
+ * All internal tx for given transaction hash
+ *
+ * @param txhash transaction hash
+ * @return internal txs list
+ * @throws EtherScanException parent exception class
+ */
+ @NotNull
+ List txsInternalByHash(@NotNull String txhash) throws EtherScanException;
+
+ /**
+ * All ERC-20 token txs for given address
+ *
+ * @param address get txs for
+ * @param startBlock tx from this blockNumber
+ * @param endBlock tx to this blockNumber
+ * @return txs for address
+ * @throws EtherScanException parent exception class
+ */
+ @NotNull
+ List txsErc20(@NotNull String address, long startBlock, long endBlock) throws EtherScanException;
+
+ @NotNull
+ List txsErc20(@NotNull String address, long startBlock) throws EtherScanException;
+
+ @NotNull
+ List txsErc20(@NotNull String address) throws EtherScanException;
+
+ /**
+ * All ERC-20 token txs for given address and contract address
+ *
+ * @param address get txs for
+ * @param contractAddress contract address to get txs for
+ * @param startBlock tx from this blockNumber
+ * @param endBlock tx to this blockNumber
+ * @return txs for address
+ * @throws EtherScanException parent exception class
+ */
+ @NotNull
+ List txsErc20(@NotNull String address, @NotNull String contractAddress, long startBlock, long endBlock)
+ throws EtherScanException;
+
+ @NotNull
+ List txsErc20(@NotNull String address, @NotNull String contractAddress, long startBlock) throws EtherScanException;
+
+ @NotNull
+ List txsErc20(@NotNull String address, @NotNull String contractAddress) throws EtherScanException;
+
+ /**
+ * All ERC-721 (NFT) token txs for given address
+ *
+ * @param address get txs for
+ * @param startBlock tx from this blockNumber
+ * @param endBlock tx to this blockNumber
+ * @return txs for address
+ * @throws EtherScanException parent exception class
+ */
+ @NotNull
+ List txsErc721(@NotNull String address, long startBlock, long endBlock) throws EtherScanException;
+
+ @NotNull
+ List txsErc721(@NotNull String address, long startBlock) throws EtherScanException;
+
+ @NotNull
+ List txsErc721(@NotNull String address) throws EtherScanException;
+
+ /**
+ * All ERC-721 (NFT) token txs for given address
+ *
+ * @param address get txs for
+ * @param startBlock tx from this blockNumber
+ * @param endBlock tx to this blockNumber
+ * @return txs for address
+ * @throws EtherScanException parent exception class
+ */
+ @NotNull
+ List txsErc721(@NotNull String address, @NotNull String contractAddress, long startBlock, long endBlock)
+ throws EtherScanException;
+
+ @NotNull
+ List txsErc721(@NotNull String address, @NotNull String contractAddress, long startBlock) throws EtherScanException;
+
+ @NotNull
+ List txsErc721(@NotNull String address, @NotNull String contractAddress) throws EtherScanException;
+
+ /**
+ * All ERC-721 (NFT) token txs for given address
+ *
+ * @param address get txs for
+ * @param startBlock tx from this blockNumber
+ * @param endBlock tx to this blockNumber
+ * @return txs for address
+ * @throws EtherScanException parent exception class
+ */
+ @NotNull
+ List txsErc1155(@NotNull String address, long startBlock, long endBlock) throws EtherScanException;
+
+ @NotNull
+ List txsErc1155(@NotNull String address, long startBlock) throws EtherScanException;
+
+ @NotNull
+ List txsErc1155(@NotNull String address) throws EtherScanException;
+
+ /**
+ * All ERC-721 (NFT) token txs for given address
+ *
+ * @param address get txs for
+ * @param startBlock tx from this blockNumber
+ * @param endBlock tx to this blockNumber
+ * @return txs for address
+ * @throws EtherScanException parent exception class
+ */
+ @NotNull
+ List txsErc1155(@NotNull String address, @NotNull String contractAddress, long startBlock, long endBlock)
+ throws EtherScanException;
+
+ @NotNull
+ List txsErc1155(@NotNull String address, @NotNull String contractAddress, long startBlock)
+ throws EtherScanException;
+
+ @NotNull
+ List txsErc1155(@NotNull String address, @NotNull String contractAddress) throws EtherScanException;
+
+ /**
+ * All blocks mined by address
+ *
+ * @param address address to search for
+ * @return blocks mined
+ * @throws EtherScanException parent exception class
+ */
+ @NotNull
+ List blocksMined(@NotNull String address) throws EtherScanException;
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java
new file mode 100644
index 0000000..442edff
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java
@@ -0,0 +1,380 @@
+package io.goodforgod.api.etherscan;
+
+import io.goodforgod.api.etherscan.error.EtherScanException;
+import io.goodforgod.api.etherscan.error.EtherScanResponseException;
+import io.goodforgod.api.etherscan.http.EthHttpClient;
+import io.goodforgod.api.etherscan.manager.RequestQueueManager;
+import io.goodforgod.api.etherscan.model.*;
+import io.goodforgod.api.etherscan.model.response.*;
+import io.goodforgod.api.etherscan.util.BasicUtils;
+import java.math.BigInteger;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Account API Implementation
+ *
+ * @see AccountAPI
+ * @author GoodforGod
+ * @since 28.10.2018
+ */
+final class AccountAPIProvider extends BasicProvider implements AccountAPI {
+
+ private static final int OFFSET_MAX = 10000;
+
+ private static final String ACT_BALANCE_ACTION = ACT_PREFIX + "balance";
+ private static final String ACT_TOKEN_BALANCE_PARAM = ACT_PREFIX + "tokenbalance";
+ private static final String ACT_BALANCE_MULTI_ACTION = ACT_PREFIX + "balancemulti";
+ private static final String ACT_TX_ACTION = ACT_PREFIX + "txlist";
+ private static final String ACT_TX_INTERNAL_ACTION = ACT_PREFIX + "txlistinternal";
+ private static final String ACT_TX_ERC20_ACTION = ACT_PREFIX + "tokentx";
+ private static final String ACT_TX_ERC721_ACTION = ACT_PREFIX + "tokennfttx";
+ private static final String ACT_TX_ERC1155_ACTION = ACT_PREFIX + "token1155tx";
+ private static final String ACT_MINED_ACTION = ACT_PREFIX + "getminedblocks";
+
+ private static final String BLOCK_TYPE_PARAM = "&blocktype=blocks";
+ private static final String CONTRACT_PARAM = "&contractaddress=";
+ private static final String START_BLOCK_PARAM = "&startblock=";
+ private static final String TAG_LATEST_PARAM = "&tag=latest";
+ private static final String END_BLOCK_PARAM = "&endblock=";
+ private static final String SORT_DESC_PARAM = "&sort=desc";
+ private static final String SORT_ASC_PARAM = "&sort=asc";
+ private static final String ADDRESS_PARAM = "&address=";
+ private static final String TXHASH_PARAM = "&txhash=";
+ private static final String OFFSET_PARAM = "&offset=";
+ private static final String PAGE_PARAM = "&page=";
+
+ AccountAPIProvider(RequestQueueManager requestQueueManager,
+ String baseUrl,
+ EthHttpClient executor,
+ Converter converter) {
+ super(requestQueueManager, "account", baseUrl, executor, converter);
+ }
+
+ @NotNull
+ @Override
+ public Balance balance(@NotNull String address) throws EtherScanException {
+ BasicUtils.validateAddress(address);
+
+ final String urlParams = ACT_BALANCE_ACTION + TAG_LATEST_PARAM + ADDRESS_PARAM + address;
+ final StringResponseTO response = getRequest(urlParams, StringResponseTO.class);
+ if (response.getStatus() != 1)
+ throw new EtherScanResponseException(response);
+
+ return new Balance(address, Wei.ofWei(new BigInteger(response.getResult())));
+ }
+
+ @NotNull
+ @Override
+ public TokenBalance balance(@NotNull String address, @NotNull String contract) throws EtherScanException {
+ BasicUtils.validateAddress(address);
+ BasicUtils.validateAddress(contract);
+
+ final String urlParams = ACT_TOKEN_BALANCE_PARAM + ADDRESS_PARAM + address + CONTRACT_PARAM + contract;
+ final StringResponseTO response = getRequest(urlParams, StringResponseTO.class);
+ if (response.getStatus() != 1)
+ throw new EtherScanResponseException(response);
+
+ return new TokenBalance(address, Wei.ofWei(new BigInteger(response.getResult())), contract);
+ }
+
+ @NotNull
+ @Override
+ public List balances(@NotNull List addresses) throws EtherScanException {
+ if (BasicUtils.isEmpty(addresses)) {
+ return Collections.emptyList();
+ }
+
+ BasicUtils.validateAddresses(addresses);
+
+ // Maximum addresses in batch request - 20
+ final List balances = new ArrayList<>();
+ final List> addressesAsBatches = BasicUtils.partition(addresses, 20);
+
+ for (final List batch : addressesAsBatches) {
+ final String urlParams = ACT_BALANCE_MULTI_ACTION + TAG_LATEST_PARAM + ADDRESS_PARAM + toAddressParam(batch);
+ final BalanceResponseTO response = getRequest(urlParams, BalanceResponseTO.class);
+ if (response.getStatus() != 1) {
+ throw new EtherScanResponseException(response);
+ }
+
+ if (!BasicUtils.isEmpty(response.getResult())) {
+ balances.addAll(response.getResult().stream()
+ .map(r -> new Balance(r.getAccount(), Wei.ofWei(new BigInteger(r.getBalance()))))
+ .collect(Collectors.toList()));
+ }
+ }
+
+ return balances;
+ }
+
+ private String toAddressParam(List addresses) {
+ return String.join(",", addresses);
+ }
+
+ @NotNull
+ @Override
+ public List txs(@NotNull String address) throws EtherScanException {
+ return txs(address, MIN_START_BLOCK);
+ }
+
+ @NotNull
+ @Override
+ public List txs(@NotNull String address, long startBlock) throws EtherScanException {
+ return txs(address, startBlock, MAX_END_BLOCK);
+ }
+
+ @NotNull
+ @Override
+ public List txs(@NotNull String address, long startBlock, long endBlock) throws EtherScanException {
+ BasicUtils.validateAddress(address);
+ final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock);
+
+ final String urlParams = ACT_TX_ACTION + PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX
+ + ADDRESS_PARAM + address
+ + START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end()
+ + SORT_ASC_PARAM;
+
+ return getRequestUsingOffset(urlParams, TxResponseTO.class);
+ }
+
+ /**
+ * Generic search for txs using offset api param To avoid 10k limit per response
+ *
+ * @param urlParams Url params for #getRequest()
+ * @param tClass responseListTO class
+ * @param responseTO list T type
+ * @param responseListTO type
+ * @return List of T values
+ */
+ private > List getRequestUsingOffset(final String urlParams, Class tClass)
+ throws EtherScanException {
+ final List result = new ArrayList<>();
+ int page = 1;
+ while (true) {
+ final String formattedUrl = String.format(urlParams, page++);
+ final R response = getRequest(formattedUrl, tClass);
+ BasicUtils.validateTxResponse(response);
+ if (BasicUtils.isEmpty(response.getResult()))
+ break;
+
+ result.addAll(response.getResult());
+ if (response.getResult().size() < OFFSET_MAX)
+ break;
+ }
+
+ return result;
+ }
+
+ @NotNull
+ @Override
+ public List txsInternal(@NotNull String address) throws EtherScanException {
+ return txsInternal(address, MIN_START_BLOCK);
+ }
+
+ @NotNull
+ @Override
+ public List txsInternal(@NotNull String address, long startBlock) throws EtherScanException {
+ return txsInternal(address, startBlock, MAX_END_BLOCK);
+ }
+
+ @NotNull
+ @Override
+ public List txsInternal(@NotNull String address, long startBlock, long endBlock)
+ throws EtherScanException {
+ BasicUtils.validateAddress(address);
+ final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock);
+
+ final String urlParams = ACT_TX_INTERNAL_ACTION + PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX
+ + ADDRESS_PARAM + address
+ + START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end()
+ + SORT_ASC_PARAM;
+
+ return getRequestUsingOffset(urlParams, TxInternalResponseTO.class);
+ }
+
+ @NotNull
+ @Override
+ public List txsInternalByHash(@NotNull String txhash) throws EtherScanException {
+ BasicUtils.validateTxHash(txhash);
+
+ final String urlParams = ACT_TX_INTERNAL_ACTION + TXHASH_PARAM + txhash;
+ final TxInternalResponseTO response = getRequest(urlParams, TxInternalResponseTO.class);
+ BasicUtils.validateTxResponse(response);
+
+ return BasicUtils.isEmpty(response.getResult())
+ ? Collections.emptyList()
+ : response.getResult();
+ }
+
+ @NotNull
+ @Override
+ public List txsErc20(@NotNull String address) throws EtherScanException {
+ return txsErc20(address, MIN_START_BLOCK);
+ }
+
+ @NotNull
+ @Override
+ public List txsErc20(@NotNull String address, long startBlock) throws EtherScanException {
+ return txsErc20(address, startBlock, MAX_END_BLOCK);
+ }
+
+ @NotNull
+ @Override
+ public List txsErc20(@NotNull String address, long startBlock, long endBlock) throws EtherScanException {
+ BasicUtils.validateAddress(address);
+ final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock);
+
+ final String urlParams = ACT_TX_ERC20_ACTION + PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX
+ + ADDRESS_PARAM + address
+ + START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end()
+ + SORT_ASC_PARAM;
+
+ return getRequestUsingOffset(urlParams, TxErc20ResponseTO.class);
+ }
+
+ @NotNull
+ @Override
+ public List txsErc20(@NotNull String address, @NotNull String contractAddress) throws EtherScanException {
+ return txsErc20(address, contractAddress, MIN_START_BLOCK);
+ }
+
+ @NotNull
+ @Override
+ public List txsErc20(@NotNull String address, @NotNull String contractAddress, long startBlock)
+ throws EtherScanException {
+ return txsErc20(address, contractAddress, startBlock, MAX_END_BLOCK);
+ }
+
+ @NotNull
+ @Override
+ public List txsErc20(@NotNull String address, @NotNull String contractAddress, long startBlock, long endBlock)
+ throws EtherScanException {
+ BasicUtils.validateAddress(address);
+ final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock);
+
+ final String offsetParam = PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX;
+ final String blockParam = START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end();
+ final String urlParams = ACT_TX_ERC20_ACTION + offsetParam + ADDRESS_PARAM + address
+ + CONTRACT_PARAM + contractAddress + blockParam + SORT_ASC_PARAM;
+
+ return getRequestUsingOffset(urlParams, TxErc20ResponseTO.class);
+ }
+
+ @NotNull
+ @Override
+ public List txsErc721(@NotNull String address) throws EtherScanException {
+ return txsErc721(address, MIN_START_BLOCK);
+ }
+
+ @NotNull
+ @Override
+ public List txsErc721(@NotNull String address, long startBlock) throws EtherScanException {
+ return txsErc721(address, startBlock, MAX_END_BLOCK);
+ }
+
+ @NotNull
+ @Override
+ public List txsErc721(@NotNull String address, long startBlock, long endBlock) throws EtherScanException {
+ BasicUtils.validateAddress(address);
+ final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock);
+
+ final String urlParams = ACT_TX_ERC721_ACTION + PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX
+ + ADDRESS_PARAM + address
+ + START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end()
+ + SORT_ASC_PARAM;
+
+ return getRequestUsingOffset(urlParams, TxErc721ResponseTO.class);
+ }
+
+ @Override
+ public @NotNull List
+ txsErc721(@NotNull String address, @NotNull String contractAddress, long startBlock, long endBlock)
+ throws EtherScanException {
+ BasicUtils.validateAddress(address);
+ final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock);
+
+ final String offsetParam = PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX;
+ final String blockParam = START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end();
+ final String urlParams = ACT_TX_ERC721_ACTION + offsetParam + ADDRESS_PARAM + address
+ + CONTRACT_PARAM + contractAddress + blockParam + SORT_ASC_PARAM;
+
+ return getRequestUsingOffset(urlParams, TxErc721ResponseTO.class);
+ }
+
+ @Override
+ public @NotNull List txsErc721(@NotNull String address, @NotNull String contractAddress, long startBlock)
+ throws EtherScanException {
+ return txsErc721(address, contractAddress, startBlock, MAX_END_BLOCK);
+ }
+
+ @Override
+ public @NotNull List txsErc721(@NotNull String address, @NotNull String contractAddress) throws EtherScanException {
+ return txsErc721(address, contractAddress, MIN_START_BLOCK);
+ }
+
+ @Override
+ public @NotNull List txsErc1155(@NotNull String address, long startBlock, long endBlock)
+ throws EtherScanException {
+ BasicUtils.validateAddress(address);
+ final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock);
+
+ final String urlParams = ACT_TX_ERC1155_ACTION + PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX
+ + ADDRESS_PARAM + address
+ + START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end()
+ + SORT_ASC_PARAM;
+
+ return getRequestUsingOffset(urlParams, TxErc1155ResponseTO.class);
+ }
+
+ @Override
+ public @NotNull List txsErc1155(@NotNull String address, long startBlock) throws EtherScanException {
+ return txsErc1155(address, startBlock, MAX_END_BLOCK);
+ }
+
+ @Override
+ public @NotNull List txsErc1155(@NotNull String address) throws EtherScanException {
+ return txsErc1155(address, MIN_START_BLOCK);
+ }
+
+ @Override
+ public @NotNull List
+ txsErc1155(@NotNull String address, @NotNull String contractAddress, long startBlock, long endBlock)
+ throws EtherScanException {
+ BasicUtils.validateAddress(address);
+ final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock);
+
+ final String offsetParam = PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX;
+ final String blockParam = START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end();
+ final String urlParams = ACT_TX_ERC1155_ACTION + offsetParam + ADDRESS_PARAM + address
+ + CONTRACT_PARAM + contractAddress + blockParam + SORT_ASC_PARAM;
+
+ return getRequestUsingOffset(urlParams, TxErc1155ResponseTO.class);
+ }
+
+ @Override
+ public @NotNull List txsErc1155(@NotNull String address, @NotNull String contractAddress, long startBlock)
+ throws EtherScanException {
+ return txsErc1155(address, contractAddress, startBlock, MAX_END_BLOCK);
+ }
+
+ @Override
+ public @NotNull List txsErc1155(@NotNull String address, @NotNull String contractAddress)
+ throws EtherScanException {
+ return txsErc1155(address, contractAddress, MIN_START_BLOCK);
+ }
+
+ @NotNull
+ @Override
+ public List blocksMined(@NotNull String address) throws EtherScanException {
+ BasicUtils.validateAddress(address);
+
+ final String urlParams = ACT_MINED_ACTION + PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX + BLOCK_TYPE_PARAM
+ + ADDRESS_PARAM + address;
+
+ return getRequestUsingOffset(urlParams, BlockResponseTO.class);
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java b/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java
new file mode 100644
index 0000000..5c61aad
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java
@@ -0,0 +1,88 @@
+package io.goodforgod.api.etherscan;
+
+import io.goodforgod.api.etherscan.error.EtherScanParseException;
+import io.goodforgod.api.etherscan.error.EtherScanRateLimitException;
+import io.goodforgod.api.etherscan.error.EtherScanResponseException;
+import io.goodforgod.api.etherscan.http.EthHttpClient;
+import io.goodforgod.api.etherscan.manager.RequestQueueManager;
+import io.goodforgod.api.etherscan.model.response.StringResponseTO;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Base provider for API Implementations
+ *
+ * @author GoodforGod
+ * @see EtherScanAPIProvider
+ * @since 28.10.2018
+ */
+abstract class BasicProvider {
+
+ private static final String MAX_RATE_LIMIT_REACHED = "Max rate limit reached";
+
+ static final int MAX_END_BLOCK = Integer.MAX_VALUE;
+ static final int MIN_START_BLOCK = 0;
+
+ static final String ACT_PREFIX = "&action=";
+
+ private final String module;
+ private final String baseUrl;
+ private final EthHttpClient executor;
+ private final RequestQueueManager queue;
+ private final Converter converter;
+
+ BasicProvider(RequestQueueManager requestQueueManager,
+ String module,
+ String baseUrl,
+ EthHttpClient ethHttpClient,
+ Converter converter) {
+ this.queue = requestQueueManager;
+ this.module = "&module=" + module;
+ this.baseUrl = baseUrl;
+ this.executor = ethHttpClient;
+ this.converter = converter;
+ }
+
+ T convert(byte[] json, Class tClass) {
+ try {
+ final T t = converter.fromJson(json, tClass);
+ if (t instanceof StringResponseTO && ((StringResponseTO) t).getResult().startsWith(MAX_RATE_LIMIT_REACHED)) {
+ throw new EtherScanRateLimitException(((StringResponseTO) t).getResult());
+ }
+
+ return t;
+ } catch (Exception e) {
+ final StringResponseTO response = converter.fromJson(json, StringResponseTO.class);
+ if (response.getResult() != null && response.getStatus() == 0) {
+ if (response.getResult().startsWith(MAX_RATE_LIMIT_REACHED)) {
+ throw new EtherScanRateLimitException(response.getResult());
+ } else {
+ throw new EtherScanResponseException(response);
+ }
+ }
+
+ final String jsonAsString = new String(json, StandardCharsets.UTF_8);
+ throw new EtherScanParseException(e.getMessage() + ", for response: " + jsonAsString, e.getCause(), jsonAsString);
+ }
+ }
+
+ byte[] getRequest(String urlParameters) {
+ queue.takeTurn();
+ final URI uri = URI.create(baseUrl + module + urlParameters);
+ return executor.get(uri);
+ }
+
+ byte[] postRequest(String urlParameters, String dataToPost) {
+ queue.takeTurn();
+ final URI uri = URI.create(baseUrl + module + urlParameters);
+ return executor.post(uri, dataToPost.getBytes(StandardCharsets.UTF_8));
+ }
+
+ T getRequest(String urlParameters, Class tClass) {
+ return convert(getRequest(urlParameters), tClass);
+ }
+
+ T postRequest(String urlParameters, String dataToPost, Class tClass) {
+ return convert(postRequest(urlParameters, dataToPost), tClass);
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/BlockAPI.java b/src/main/java/io/goodforgod/api/etherscan/BlockAPI.java
new file mode 100644
index 0000000..fdacaf5
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/BlockAPI.java
@@ -0,0 +1,25 @@
+package io.goodforgod.api.etherscan;
+
+import io.goodforgod.api.etherscan.error.EtherScanException;
+import io.goodforgod.api.etherscan.model.BlockUncle;
+import java.util.Optional;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * EtherScan - API Descriptions ...
+ *
+ * @author GoodforGod
+ * @since 30.10.2018
+ */
+public interface BlockAPI {
+
+ /**
+ * Return uncle blocks
+ *
+ * @param blockNumber block number form 0 to last
+ * @return optional uncle blocks
+ * @throws EtherScanException parent exception class
+ */
+ @NotNull
+ Optional uncles(long blockNumber) throws EtherScanException;
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java
new file mode 100644
index 0000000..406ac19
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java
@@ -0,0 +1,60 @@
+package io.goodforgod.api.etherscan;
+
+import io.goodforgod.api.etherscan.error.EtherScanException;
+import io.goodforgod.api.etherscan.error.EtherScanResponseException;
+import io.goodforgod.api.etherscan.http.EthHttpClient;
+import io.goodforgod.api.etherscan.manager.RequestQueueManager;
+import io.goodforgod.api.etherscan.model.BlockUncle;
+import io.goodforgod.api.etherscan.model.response.UncleBlockResponseTO;
+import io.goodforgod.api.etherscan.util.BasicUtils;
+import java.util.Optional;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Block API Implementation
+ *
+ * @author GoodforGod
+ * @see BlockAPI
+ * @since 28.10.2018
+ */
+final class BlockAPIProvider extends BasicProvider implements BlockAPI {
+
+ private static final String ACT_BLOCK_PARAM = ACT_PREFIX + "getblockreward";
+
+ private static final String BLOCKNO_PARAM = "&blockno=";
+
+ BlockAPIProvider(RequestQueueManager requestQueueManager,
+ String baseUrl,
+ EthHttpClient executor,
+ Converter converter) {
+ super(requestQueueManager, "block", baseUrl, executor, converter);
+ }
+
+ @NotNull
+ @Override
+ public Optional uncles(long blockNumber) throws EtherScanException {
+ final String urlParam = ACT_BLOCK_PARAM + BLOCKNO_PARAM + blockNumber;
+ final byte[] response = getRequest(urlParam);
+ if (response.length == 0) {
+ return Optional.empty();
+ }
+
+ try {
+ final UncleBlockResponseTO responseTO = convert(response, UncleBlockResponseTO.class);
+ if (responseTO.getMessage().startsWith("NOTOK")) {
+ return Optional.empty();
+ }
+
+ BasicUtils.validateTxResponse(responseTO);
+ return (responseTO.getResult() == null || responseTO.getResult().isEmpty())
+ ? Optional.empty()
+ : Optional.of(responseTO.getResult());
+ } catch (EtherScanResponseException e) {
+ if (e.getResponse().getMessage().startsWith("NOTOK")) {
+ return Optional.empty();
+ } else {
+ throw e;
+ }
+ }
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/ContractAPI.java b/src/main/java/io/goodforgod/api/etherscan/ContractAPI.java
new file mode 100644
index 0000000..af0852c
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/ContractAPI.java
@@ -0,0 +1,24 @@
+package io.goodforgod.api.etherscan;
+
+import io.goodforgod.api.etherscan.error.EtherScanException;
+import io.goodforgod.api.etherscan.model.Abi;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * EtherScan - API Descriptions ...
+ *
+ * @author GoodforGod
+ * @since 28.10.2018
+ */
+public interface ContractAPI {
+
+ /**
+ * Get Verified Contract Sources
+ *
+ * @param address to verify
+ * @return ABI verified
+ * @throws EtherScanException parent exception class
+ */
+ @NotNull
+ Abi contractAbi(@NotNull String address) throws EtherScanException;
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java
new file mode 100644
index 0000000..6b4404a
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java
@@ -0,0 +1,47 @@
+package io.goodforgod.api.etherscan;
+
+import io.goodforgod.api.etherscan.error.EtherScanException;
+import io.goodforgod.api.etherscan.error.EtherScanResponseException;
+import io.goodforgod.api.etherscan.http.EthHttpClient;
+import io.goodforgod.api.etherscan.manager.RequestQueueManager;
+import io.goodforgod.api.etherscan.model.Abi;
+import io.goodforgod.api.etherscan.model.response.StringResponseTO;
+import io.goodforgod.api.etherscan.util.BasicUtils;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Contract API Implementation
+ *
+ * @see ContractAPI
+ * @author GoodforGod
+ * @since 28.10.2018
+ */
+final class ContractAPIProvider extends BasicProvider implements ContractAPI {
+
+ private static final String ACT_ABI_PARAM = ACT_PREFIX + "getabi";
+
+ private static final String ADDRESS_PARAM = "&address=";
+
+ ContractAPIProvider(RequestQueueManager requestQueueManager,
+ String baseUrl,
+ EthHttpClient executor,
+ Converter converter) {
+ super(requestQueueManager, "contract", baseUrl, executor, converter);
+ }
+
+ @NotNull
+ @Override
+ public Abi contractAbi(@NotNull String address) throws EtherScanException {
+ BasicUtils.validateAddress(address);
+
+ final String urlParam = ACT_ABI_PARAM + ADDRESS_PARAM + address;
+ final StringResponseTO response = getRequest(urlParam, StringResponseTO.class);
+ if (response.getStatus() != 1 && response.getMessage().startsWith("NOTOK")) {
+ throw new EtherScanResponseException(response);
+ }
+
+ return (response.getResult().startsWith("Contract sou"))
+ ? Abi.nonVerified()
+ : Abi.verified(response.getResult());
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/Converter.java b/src/main/java/io/goodforgod/api/etherscan/Converter.java
new file mode 100644
index 0000000..4025839
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/Converter.java
@@ -0,0 +1,13 @@
+package io.goodforgod.api.etherscan;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Anton Kurako (GoodforGod)
+ * @since 14.05.2023
+ */
+public interface Converter {
+
+ @NotNull
+ T fromJson(byte[] jsonAsByteArray, @NotNull Class type);
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/EthNetwork.java b/src/main/java/io/goodforgod/api/etherscan/EthNetwork.java
new file mode 100644
index 0000000..96d8d0b
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/EthNetwork.java
@@ -0,0 +1,17 @@
+package io.goodforgod.api.etherscan;
+
+import java.net.URI;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Anton Kurako (GoodforGod)
+ * @since 11.05.2023
+ */
+public interface EthNetwork {
+
+ /**
+ * @return URI for network domain like EtherScan API
+ */
+ @NotNull
+ URI domain();
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/EthNetworks.java b/src/main/java/io/goodforgod/api/etherscan/EthNetworks.java
new file mode 100644
index 0000000..9e18508
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/EthNetworks.java
@@ -0,0 +1,26 @@
+package io.goodforgod.api.etherscan;
+
+import java.net.URI;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author GoodforGod
+ * @since 28.10.2018
+ */
+public enum EthNetworks implements EthNetwork {
+
+ MAINNET("api", "io"),
+ GORLI("api-goerli", "io"),
+ SEPOLIA("api-sepolia", "io");
+
+ private final URI domain;
+
+ EthNetworks(String domain, String extension) {
+ this.domain = URI.create("https://" + domain + ".etherscan." + extension + "/api");
+ }
+
+ @Override
+ public @NotNull URI domain() {
+ return domain;
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java b/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java
new file mode 100644
index 0000000..dad9c50
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java
@@ -0,0 +1,104 @@
+package io.goodforgod.api.etherscan;
+
+import com.google.gson.Gson;
+import io.goodforgod.api.etherscan.error.EtherScanKeyException;
+import io.goodforgod.api.etherscan.error.EtherScanParseException;
+import io.goodforgod.api.etherscan.http.EthHttpClient;
+import io.goodforgod.api.etherscan.http.impl.UrlEthHttpClient;
+import io.goodforgod.api.etherscan.manager.RequestQueueManager;
+import io.goodforgod.api.etherscan.util.BasicUtils;
+import io.goodforgod.gson.configuration.GsonConfiguration;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.function.Supplier;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Anton Kurako (GoodforGod)
+ * @since 11.05.2023
+ */
+final class EthScanAPIBuilder implements EtherScanAPI.Builder {
+
+ private static final Supplier DEFAULT_SUPPLIER = UrlEthHttpClient::new;
+ private static final String DEFAULT_KEY = "YourApiKeyToken";
+
+ private final Gson gson = new GsonConfiguration().builder().create();
+
+ private String apiKey = DEFAULT_KEY;
+ private RequestQueueManager queueManager;
+ private EthNetwork ethNetwork = EthNetworks.MAINNET;
+ private Supplier ethHttpClientSupplier = DEFAULT_SUPPLIER;
+ private Supplier converterSupplier = () -> new Converter() {
+
+ @Override
+ public @NotNull T fromJson(byte[] jsonAsByteArray, @NotNull Class type) {
+ try (InputStreamReader isr = new InputStreamReader(new ByteArrayInputStream(jsonAsByteArray))) {
+ return gson.fromJson(isr, type);
+ } catch (IOException e) {
+ throw new EtherScanParseException(e.getMessage(), e, new String(jsonAsByteArray, StandardCharsets.UTF_8));
+ }
+ }
+ };
+
+ @NotNull
+ @Override
+ public EtherScanAPI.Builder withApiKey(@NotNull String apiKey) {
+ if (BasicUtils.isBlank(apiKey))
+ throw new EtherScanKeyException("API key can not be null or empty");
+
+ this.apiKey = apiKey;
+ return this;
+ }
+
+ @NotNull
+ @Override
+ public EtherScanAPI.Builder withNetwork(@NotNull EthNetwork network) {
+ this.ethNetwork = network;
+ return this;
+ }
+
+ @NotNull
+ @Override
+ public EtherScanAPI.Builder withNetwork(@NotNull EthNetworks network) {
+ this.ethNetwork = network;
+ return this;
+ }
+
+ @NotNull
+ @Override
+ public EtherScanAPI.Builder withQueue(@NotNull RequestQueueManager queueManager) {
+ this.queueManager = queueManager;
+ return this;
+ }
+
+ @NotNull
+ @Override
+ public EtherScanAPI.Builder withHttpClient(@NotNull Supplier httpClientSupplier) {
+ this.ethHttpClientSupplier = httpClientSupplier;
+ return this;
+ }
+
+ @NotNull
+ @Override
+ public EtherScanAPI.Builder withConverter(@NotNull Supplier converterSupplier) {
+ this.converterSupplier = converterSupplier;
+ return this;
+ }
+
+ @Override
+ public @NotNull EtherScanAPI build() {
+ RequestQueueManager requestQueueManager;
+ if (queueManager != null) {
+ requestQueueManager = queueManager;
+ } else if (DEFAULT_KEY.equals(apiKey)) {
+ requestQueueManager = RequestQueueManager.anonymous();
+ } else {
+ requestQueueManager = RequestQueueManager.planFree();
+ }
+
+ return new EtherScanAPIProvider(apiKey, ethNetwork, requestQueueManager, ethHttpClientSupplier.get(),
+ converterSupplier.get());
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java
new file mode 100644
index 0000000..6da3d8f
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java
@@ -0,0 +1,68 @@
+package io.goodforgod.api.etherscan;
+
+import io.goodforgod.api.etherscan.http.EthHttpClient;
+import io.goodforgod.api.etherscan.manager.RequestQueueManager;
+import java.util.function.Supplier;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * EtherScan full API Description ...
+ *
+ * @author GoodforGod
+ * @since 10.05.2023
+ */
+public interface EtherScanAPI extends AutoCloseable {
+
+ @NotNull
+ AccountAPI account();
+
+ @NotNull
+ ContractAPI contract();
+
+ @NotNull
+ TransactionAPI txs();
+
+ @NotNull
+ BlockAPI block();
+
+ @NotNull
+ LogsAPI logs();
+
+ @NotNull
+ ProxyAPI proxy();
+
+ @NotNull
+ StatisticAPI stats();
+
+ @NotNull
+ GasTrackerAPI gasTracker();
+
+ @NotNull
+ static Builder builder() {
+ return new EthScanAPIBuilder();
+ }
+
+ interface Builder {
+
+ @NotNull
+ Builder withApiKey(@NotNull String apiKey);
+
+ @NotNull
+ Builder withNetwork(@NotNull EthNetwork network);
+
+ @NotNull
+ Builder withNetwork(@NotNull EthNetworks network);
+
+ @NotNull
+ Builder withQueue(@NotNull RequestQueueManager queueManager);
+
+ @NotNull
+ Builder withHttpClient(@NotNull Supplier httpClientSupplier);
+
+ @NotNull
+ Builder withConverter(@NotNull Supplier converterSupplier);
+
+ @NotNull
+ EtherScanAPI build();
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/EtherScanAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPIProvider.java
new file mode 100644
index 0000000..e698f45
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPIProvider.java
@@ -0,0 +1,95 @@
+package io.goodforgod.api.etherscan;
+
+import io.goodforgod.api.etherscan.http.EthHttpClient;
+import io.goodforgod.api.etherscan.manager.RequestQueueManager;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * EtherScan full API Description ...
+ *
+ * @author GoodforGod
+ * @since 28.10.2018
+ */
+final class EtherScanAPIProvider implements EtherScanAPI {
+
+ private final RequestQueueManager requestQueueManager;
+ private final AccountAPI account;
+ private final BlockAPI block;
+ private final ContractAPI contract;
+ private final LogsAPI logs;
+ private final ProxyAPI proxy;
+ private final StatisticAPI stats;
+ private final TransactionAPI txs;
+ private final GasTrackerAPI gasTracker;
+
+ EtherScanAPIProvider(String apiKey,
+ EthNetwork network,
+ RequestQueueManager queue,
+ EthHttpClient ethHttpClient,
+ Converter converter) {
+ // EtherScan 1request\5sec limit support by queue manager
+ final String baseUrl = network.domain() + "?apikey=" + apiKey;
+
+ this.requestQueueManager = queue;
+ this.account = new AccountAPIProvider(queue, baseUrl, ethHttpClient, converter);
+ this.block = new BlockAPIProvider(queue, baseUrl, ethHttpClient, converter);
+ this.contract = new ContractAPIProvider(queue, baseUrl, ethHttpClient, converter);
+ this.logs = new LogsAPIProvider(queue, baseUrl, ethHttpClient, converter);
+ this.proxy = new ProxyAPIProvider(queue, baseUrl, ethHttpClient, converter);
+ this.stats = new StatisticAPIProvider(queue, baseUrl, ethHttpClient, converter);
+ this.txs = new TransactionAPIProvider(queue, baseUrl, ethHttpClient, converter);
+ this.gasTracker = new GasTrackerAPIProvider(queue, baseUrl, ethHttpClient, converter);
+ }
+
+ @NotNull
+ @Override
+ public AccountAPI account() {
+ return account;
+ }
+
+ @NotNull
+ @Override
+ public ContractAPI contract() {
+ return contract;
+ }
+
+ @NotNull
+ @Override
+ public TransactionAPI txs() {
+ return txs;
+ }
+
+ @NotNull
+ @Override
+ public BlockAPI block() {
+ return block;
+ }
+
+ @NotNull
+ @Override
+ public LogsAPI logs() {
+ return logs;
+ }
+
+ @NotNull
+ @Override
+ public ProxyAPI proxy() {
+ return proxy;
+ }
+
+ @NotNull
+ @Override
+ public StatisticAPI stats() {
+ return stats;
+ }
+
+ @Override
+ public @NotNull GasTrackerAPI gasTracker() {
+ return gasTracker;
+ }
+
+ @Override
+ public void close() throws Exception {
+ requestQueueManager.close();
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPI.java b/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPI.java
new file mode 100644
index 0000000..6fce763
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPI.java
@@ -0,0 +1,35 @@
+package io.goodforgod.api.etherscan;
+
+import io.goodforgod.api.etherscan.error.EtherScanException;
+import io.goodforgod.api.etherscan.model.GasOracle;
+import io.goodforgod.api.etherscan.model.Wei;
+import java.time.Duration;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * EtherScan - API Descriptions
+ * ...
+ *
+ * @author Abhay Gupta
+ * @since 14.11.2022
+ */
+public interface GasTrackerAPI {
+
+ /**
+ * Returns the estimated time for a transaction to be confirmed on the blockchain.
+ *
+ * @return estimated time
+ * @throws EtherScanException parent exception class
+ */
+ @NotNull
+ Duration estimate(@NotNull Wei wei) throws EtherScanException;
+
+ /**
+ * Returns the current Safe, Proposed and Fast gas prices.
+ *
+ * @return fast, suggested gas price
+ * @throws EtherScanException parent exception class
+ */
+ @NotNull
+ GasOracle oracle() throws EtherScanException;
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java
new file mode 100644
index 0000000..cbe0a75
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java
@@ -0,0 +1,54 @@
+package io.goodforgod.api.etherscan;
+
+import io.goodforgod.api.etherscan.error.EtherScanException;
+import io.goodforgod.api.etherscan.error.EtherScanResponseException;
+import io.goodforgod.api.etherscan.http.EthHttpClient;
+import io.goodforgod.api.etherscan.manager.RequestQueueManager;
+import io.goodforgod.api.etherscan.model.GasOracle;
+import io.goodforgod.api.etherscan.model.Wei;
+import io.goodforgod.api.etherscan.model.response.GasEstimateResponseTO;
+import io.goodforgod.api.etherscan.model.response.GasOracleResponseTO;
+import java.time.Duration;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * GasTracker API Implementation
+ *
+ * @see GasTrackerAPI
+ * @author Abhay Gupta
+ * @since 14.11.2022
+ */
+final class GasTrackerAPIProvider extends BasicProvider implements GasTrackerAPI {
+
+ private static final String ACT_GAS_ORACLE_PARAM = ACT_PREFIX + "gasoracle";
+ private static final String ACT_GAS_ESTIMATE_PARAM = ACT_PREFIX + "gasestimate";
+
+ private static final String GASPRICE_PARAM = "&gasprice=";
+
+ GasTrackerAPIProvider(RequestQueueManager queue,
+ String baseUrl,
+ EthHttpClient ethHttpClient,
+ Converter converter) {
+ super(queue, "gastracker", baseUrl, ethHttpClient, converter);
+ }
+
+ @Override
+ public @NotNull Duration estimate(@NotNull Wei wei) throws EtherScanException {
+ final String urlParams = ACT_GAS_ESTIMATE_PARAM + GASPRICE_PARAM + wei.asWei().toString();
+ final GasEstimateResponseTO response = getRequest(urlParams, GasEstimateResponseTO.class);
+ if (response.getStatus() != 1)
+ throw new EtherScanResponseException(response);
+
+ return Duration.ofSeconds(Long.parseLong(response.getResult()));
+ }
+
+ @NotNull
+ @Override
+ public GasOracle oracle() throws EtherScanException {
+ final GasOracleResponseTO response = getRequest(ACT_GAS_ORACLE_PARAM, GasOracleResponseTO.class);
+ if (response.getStatus() != 1)
+ throw new EtherScanResponseException(response);
+
+ return response.getResult();
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/LogsAPI.java b/src/main/java/io/goodforgod/api/etherscan/LogsAPI.java
new file mode 100644
index 0000000..0330f9f
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/LogsAPI.java
@@ -0,0 +1,27 @@
+package io.goodforgod.api.etherscan;
+
+import io.goodforgod.api.etherscan.error.EtherScanException;
+import io.goodforgod.api.etherscan.model.Log;
+import io.goodforgod.api.etherscan.model.query.LogQuery;
+import java.util.List;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * EtherScan - API Descriptions ...
+ *
+ * @author GoodforGod
+ * @since 30.10.2018
+ */
+public interface LogsAPI {
+
+ /**
+ * alternative to the native eth_getLogs Read at EtherScan API description for full info!
+ *
+ * @param query build log query
+ * @return logs according to query
+ * @throws EtherScanException parent exception class
+ * @see LogQuery
+ */
+ @NotNull
+ List logs(@NotNull LogQuery query) throws EtherScanException;
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/LogsAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/LogsAPIProvider.java
new file mode 100644
index 0000000..d294fb5
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/LogsAPIProvider.java
@@ -0,0 +1,43 @@
+package io.goodforgod.api.etherscan;
+
+import io.goodforgod.api.etherscan.error.EtherScanException;
+import io.goodforgod.api.etherscan.http.EthHttpClient;
+import io.goodforgod.api.etherscan.manager.RequestQueueManager;
+import io.goodforgod.api.etherscan.model.Log;
+import io.goodforgod.api.etherscan.model.query.LogQuery;
+import io.goodforgod.api.etherscan.model.response.LogResponseTO;
+import io.goodforgod.api.etherscan.util.BasicUtils;
+import java.util.Collections;
+import java.util.List;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Logs API Implementation
+ *
+ * @see LogsAPI
+ * @author GoodforGod
+ * @since 28.10.2018
+ */
+final class LogsAPIProvider extends BasicProvider implements LogsAPI {
+
+ private static final String ACT_LOGS_PARAM = ACT_PREFIX + "getLogs";
+
+ LogsAPIProvider(RequestQueueManager queue,
+ String baseUrl,
+ EthHttpClient executor,
+ Converter converter) {
+ super(queue, "logs", baseUrl, executor, converter);
+ }
+
+ @NotNull
+ @Override
+ public List logs(@NotNull LogQuery query) throws EtherScanException {
+ final String urlParams = ACT_LOGS_PARAM + query.params();
+ final LogResponseTO response = getRequest(urlParams, LogResponseTO.class);
+ BasicUtils.validateTxResponse(response);
+
+ return (BasicUtils.isEmpty(response.getResult()))
+ ? Collections.emptyList()
+ : response.getResult();
+ }
+}
diff --git a/src/main/java/io/api/etherscan/core/IProxyApi.java b/src/main/java/io/goodforgod/api/etherscan/ProxyAPI.java
similarity index 61%
rename from src/main/java/io/api/etherscan/core/IProxyApi.java
rename to src/main/java/io/goodforgod/api/etherscan/ProxyAPI.java
index 6adcdf0..30c4f96 100644
--- a/src/main/java/io/api/etherscan/core/IProxyApi.java
+++ b/src/main/java/io/goodforgod/api/etherscan/ProxyAPI.java
@@ -1,28 +1,28 @@
-package io.api.etherscan.core;
+package io.goodforgod.api.etherscan;
-import io.api.etherscan.error.ApiException;
-import io.api.etherscan.model.proxy.BlockProxy;
-import io.api.etherscan.model.proxy.ReceiptProxy;
-import io.api.etherscan.model.proxy.TxProxy;
+import io.goodforgod.api.etherscan.error.EtherScanException;
+import io.goodforgod.api.etherscan.model.Wei;
+import io.goodforgod.api.etherscan.model.proxy.BlockProxy;
+import io.goodforgod.api.etherscan.model.proxy.ReceiptProxy;
+import io.goodforgod.api.etherscan.model.proxy.TxProxy;
+import java.util.Optional;
import org.jetbrains.annotations.ApiStatus.Experimental;
import org.jetbrains.annotations.NotNull;
-import java.math.BigInteger;
-import java.util.Optional;
-
/**
- * EtherScan - API Descriptions https://etherscan.io/apis#proxy
+ * EtherScan - API Descriptions
+ * ...
*
* @author GoodforGod
* @since 30.10.2018
*/
-public interface IProxyApi {
+public interface ProxyAPI {
/**
* Returns the number of most recent block eth_blockNumber
*
* @return last block number
- * @throws ApiException parent exception class
+ * @throws EtherScanException parent exception class
*/
long blockNoLast();
@@ -31,10 +31,10 @@ public interface IProxyApi {
*
* @param blockNo block number from 0 to last
* @return optional block result
- * @throws ApiException parent exception class
+ * @throws EtherScanException parent exception class
*/
@NotNull
- Optional block(long blockNo) throws ApiException;
+ Optional block(long blockNo) throws EtherScanException;
/**
* Returns information about a uncle by block number eth_getUncleByBlockNumberAndIndex
@@ -42,10 +42,10 @@ public interface IProxyApi {
* @param blockNo block number from 0 to last
* @param index uncle block index
* @return optional block result
- * @throws ApiException parent exception class
+ * @throws EtherScanException parent exception class
*/
@NotNull
- Optional blockUncle(long blockNo, long index) throws ApiException;
+ Optional blockUncle(long blockNo, long index) throws EtherScanException;
/**
* Returns the information about a transaction requested by transaction hash
@@ -53,10 +53,10 @@ public interface IProxyApi {
*
* @param txhash transaction hash
* @return optional tx result
- * @throws ApiException parent exception class
+ * @throws EtherScanException parent exception class
*/
@NotNull
- Optional tx(String txhash) throws ApiException;
+ Optional tx(@NotNull String txhash) throws EtherScanException;
/**
* Returns information about a transaction by block number and transaction index position
@@ -65,10 +65,10 @@ public interface IProxyApi {
* @param blockNo block number from 0 to last
* @param index tx index in block
* @return optional tx result
- * @throws ApiException parent exception class
+ * @throws EtherScanException parent exception class
*/
@NotNull
- Optional tx(long blockNo, long index) throws ApiException;
+ Optional tx(long blockNo, long index) throws EtherScanException;
/**
* Returns the number of transactions in a block from a block matching the given block number
@@ -76,18 +76,18 @@ public interface IProxyApi {
*
* @param blockNo block number from 0 to last
* @return transaction amount in block
- * @throws ApiException parent exception class
+ * @throws EtherScanException parent exception class
*/
- int txCount(long blockNo) throws ApiException;
+ int txCount(long blockNo) throws EtherScanException;
/**
* Returns the number of transactions sent from an address eth_getTransactionCount
*
* @param address eth address
* @return transactions send amount from address
- * @throws ApiException parent exception class
+ * @throws EtherScanException parent exception class
*/
- int txSendCount(String address) throws ApiException;
+ int txSendCount(@NotNull String address) throws EtherScanException;
/**
* Creates new message call transaction or a contract creation for signed transactions
@@ -95,20 +95,20 @@ public interface IProxyApi {
*
* @param hexEncodedTx encoded hex data to send
* @return optional string response
- * @throws ApiException parent exception class
+ * @throws EtherScanException parent exception class
*/
@NotNull
- Optional txSendRaw(String hexEncodedTx) throws ApiException;
+ Optional txSendRaw(@NotNull String hexEncodedTx) throws EtherScanException;
/**
* Returns the receipt of a transaction by transaction hash eth_getTransactionReceipt
*
* @param txhash transaction hash
* @return optional tx receipt
- * @throws ApiException parent exception class
+ * @throws EtherScanException parent exception class
*/
@NotNull
- Optional txReceipt(String txhash) throws ApiException;
+ Optional txReceipt(@NotNull String txhash) throws EtherScanException;
/**
* Executes a new message call immediately without creating a transaction on the block chain
@@ -117,20 +117,20 @@ public interface IProxyApi {
* @param address to call
* @param data data to call address
* @return optional the return value of executed contract.
- * @throws ApiException parent exception class
+ * @throws EtherScanException parent exception class
*/
@NotNull
- Optional call(String address, String data) throws ApiException;
+ Optional call(@NotNull String address, @NotNull String data) throws EtherScanException;
/**
* Returns code at a given address eth_getCode
*
* @param address get code from
* @return optional the code from the given address
- * @throws ApiException parent exception class
+ * @throws EtherScanException parent exception class
*/
@NotNull
- Optional code(String address) throws ApiException;
+ Optional code(@NotNull String address) throws EtherScanException;
/**
* (**experimental) Returns the value from a storage position at a given address eth_getStorageAt
@@ -138,20 +138,20 @@ public interface IProxyApi {
* @param address to get storage
* @param position storage position
* @return optional the value at this storage position
- * @throws ApiException parent exception class
+ * @throws EtherScanException parent exception class
*/
@Experimental
@NotNull
- Optional storageAt(String address, long position) throws ApiException;
+ Optional storageAt(@NotNull String address, long position) throws EtherScanException;
/**
* Returns the current price per gas in wei eth_gasPrice
*
* @return estimated gas price
- * @throws ApiException parent exception class
+ * @throws EtherScanException parent exception class
*/
@NotNull
- BigInteger gasPrice() throws ApiException;
+ Wei gasPrice() throws EtherScanException;
/**
* Makes a call or transaction, which won't be added to the blockchain and returns the used gas,
@@ -159,11 +159,11 @@ public interface IProxyApi {
*
* @param hexData data to calc gas usage for
* @return estimated gas usage
- * @throws ApiException parent exception class
+ * @throws EtherScanException parent exception class
*/
@NotNull
- BigInteger gasEstimated(String hexData) throws ApiException;
+ Wei gasEstimated(@NotNull String hexData) throws EtherScanException;
@NotNull
- BigInteger gasEstimated() throws ApiException;
+ Wei gasEstimated() throws EtherScanException;
}
diff --git a/src/main/java/io/api/etherscan/core/impl/ProxyApiProvider.java b/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java
similarity index 67%
rename from src/main/java/io/api/etherscan/core/impl/ProxyApiProvider.java
rename to src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java
index cb0c6a5..4dff589 100644
--- a/src/main/java/io/api/etherscan/core/impl/ProxyApiProvider.java
+++ b/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java
@@ -1,34 +1,32 @@
-package io.api.etherscan.core.impl;
-
-import io.api.etherscan.core.IProxyApi;
-import io.api.etherscan.error.ApiException;
-import io.api.etherscan.error.EtherScanException;
-import io.api.etherscan.error.InvalidDataHexException;
-import io.api.etherscan.executor.IHttpExecutor;
-import io.api.etherscan.manager.IQueueManager;
-import io.api.etherscan.model.proxy.BlockProxy;
-import io.api.etherscan.model.proxy.ReceiptProxy;
-import io.api.etherscan.model.proxy.TxProxy;
-import io.api.etherscan.model.proxy.utility.BlockProxyTO;
-import io.api.etherscan.model.proxy.utility.StringProxyTO;
-import io.api.etherscan.model.proxy.utility.TxInfoProxyTO;
-import io.api.etherscan.model.proxy.utility.TxProxyTO;
-import io.api.etherscan.util.BasicUtils;
-import org.jetbrains.annotations.NotNull;
-
-import java.math.BigInteger;
+package io.goodforgod.api.etherscan;
+
+import io.goodforgod.api.etherscan.error.EtherScanException;
+import io.goodforgod.api.etherscan.error.EtherScanInvalidDataHexException;
+import io.goodforgod.api.etherscan.error.EtherScanResponseException;
+import io.goodforgod.api.etherscan.http.EthHttpClient;
+import io.goodforgod.api.etherscan.manager.RequestQueueManager;
+import io.goodforgod.api.etherscan.model.Wei;
+import io.goodforgod.api.etherscan.model.proxy.BlockProxy;
+import io.goodforgod.api.etherscan.model.proxy.ReceiptProxy;
+import io.goodforgod.api.etherscan.model.proxy.TxProxy;
+import io.goodforgod.api.etherscan.model.proxy.utility.BlockProxyTO;
+import io.goodforgod.api.etherscan.model.proxy.utility.StringProxyTO;
+import io.goodforgod.api.etherscan.model.proxy.utility.TxInfoProxyTO;
+import io.goodforgod.api.etherscan.model.proxy.utility.TxProxyTO;
+import io.goodforgod.api.etherscan.model.response.StringResponseTO;
+import io.goodforgod.api.etherscan.util.BasicUtils;
import java.util.Optional;
import java.util.regex.Pattern;
+import org.jetbrains.annotations.NotNull;
/**
* Proxy API Implementation
*
- * @see IProxyApi
- *
+ * @see ProxyAPI
* @author GoodforGod
* @since 28.10.2018
*/
-public class ProxyApiProvider extends BasicProvider implements IProxyApi {
+final class ProxyAPIProvider extends BasicProvider implements ProxyAPI {
private static final String ACT_BLOCKNO_PARAM = ACT_PREFIX + "eth_blockNumber";
private static final String ACT_BY_BLOCKNO_PARAM = ACT_PREFIX + "eth_getBlockByNumber";
@@ -59,14 +57,15 @@ public class ProxyApiProvider extends BasicProvider implements IProxyApi {
private static final Pattern EMPTY_HEX = Pattern.compile("0x0+");
- ProxyApiProvider(final IQueueManager queue,
- final String baseUrl,
- final IHttpExecutor executor) {
- super(queue, "proxy", baseUrl, executor);
+ ProxyAPIProvider(RequestQueueManager queue,
+ String baseUrl,
+ EthHttpClient executor,
+ Converter converter) {
+ super(queue, "proxy", baseUrl, executor, converter);
}
@Override
- public long blockNoLast() throws ApiException {
+ public long blockNoLast() throws EtherScanException {
final StringProxyTO response = getRequest(ACT_BLOCKNO_PARAM, StringProxyTO.class);
return (BasicUtils.isEmpty(response.getResult()))
? -1
@@ -75,7 +74,7 @@ public long blockNoLast() throws ApiException {
@NotNull
@Override
- public Optional block(final long blockNo) throws ApiException {
+ public Optional block(long blockNo) throws EtherScanException {
final long compBlockNo = BasicUtils.compensateMinBlock(blockNo);
final String urlParams = ACT_BY_BLOCKNO_PARAM + TAG_PARAM + compBlockNo + BOOLEAN_PARAM;
@@ -85,7 +84,7 @@ public Optional block(final long blockNo) throws ApiException {
@NotNull
@Override
- public Optional blockUncle(final long blockNo, final long index) throws ApiException {
+ public Optional blockUncle(long blockNo, long index) throws EtherScanException {
final long compBlockNo = BasicUtils.compensateMinBlock(blockNo);
final long compIndex = BasicUtils.compensateMinBlock(index);
@@ -97,7 +96,7 @@ public Optional blockUncle(final long blockNo, final long index) thr
@NotNull
@Override
- public Optional tx(final String txhash) throws ApiException {
+ public Optional tx(@NotNull String txhash) throws EtherScanException {
BasicUtils.validateTxHash(txhash);
final String urlParams = ACT_TX_BY_HASH_PARAM + TXHASH_PARAM + txhash;
@@ -107,9 +106,11 @@ public Optional tx(final String txhash) throws ApiException {
@NotNull
@Override
- public Optional tx(final long blockNo, final long index) throws ApiException {
+ public Optional tx(long blockNo, long index) throws EtherScanException {
final long compBlockNo = BasicUtils.compensateMinBlock(blockNo);
- final long compIndex = (index < 1) ? 1 : index;
+ final long compIndex = (index < 1)
+ ? 1
+ : index;
final String urlParams = ACT_TX_BY_BLOCKNOINDEX_PARAM + TAG_PARAM + compBlockNo + INDEX_PARAM + "0x"
+ Long.toHexString(compIndex);
@@ -118,7 +119,7 @@ public Optional tx(final long blockNo, final long index) throws ApiExce
}
@Override
- public int txCount(final long blockNo) throws ApiException {
+ public int txCount(long blockNo) throws EtherScanException {
final long compensatedBlockNo = BasicUtils.compensateMinBlock(blockNo);
final String urlParams = ACT_BLOCKTX_COUNT_PARAM + TAG_PARAM + "0x" + Long.toHexString(compensatedBlockNo);
final StringProxyTO response = getRequest(urlParams, StringProxyTO.class);
@@ -126,7 +127,7 @@ public int txCount(final long blockNo) throws ApiException {
}
@Override
- public int txSendCount(final String address) throws ApiException {
+ public int txSendCount(@NotNull String address) throws EtherScanException {
BasicUtils.validateAddress(address);
final String urlParams = ACT_TX_COUNT_PARAM + ADDRESS_PARAM + address + TAG_LAST_PARAM;
@@ -136,23 +137,30 @@ public int txSendCount(final String address) throws ApiException {
@Override
@NotNull
- public Optional txSendRaw(final String hexEncodedTx) throws ApiException {
+ public Optional txSendRaw(@NotNull String hexEncodedTx) throws EtherScanException {
if (BasicUtils.isNotHex(hexEncodedTx))
- throw new InvalidDataHexException("Data is not encoded in hex format - " + hexEncodedTx);
+ throw new EtherScanInvalidDataHexException("Data is not encoded in hex format - " + hexEncodedTx);
final String urlParams = ACT_SEND_RAW_TX_PARAM + HEX_PARAM + hexEncodedTx;
final StringProxyTO response = postRequest(urlParams, "", StringProxyTO.class);
- if (response.getError() != null)
- throw new EtherScanException("Error occurred with code " + response.getError().getCode()
+ if (response.getError() != null) {
+ final StringResponseTO responseError = StringResponseTO.builder()
+ .withStatus("0")
+ .withMessage(response.getError().getMessage())
+ .withResult(response.getError().getCode())
+ .build();
+
+ throw new EtherScanResponseException(responseError, "Error occurred with code " + response.getError().getCode()
+ " with message " + response.getError().getMessage()
+ ", error id " + response.getId() + ", jsonRPC " + response.getJsonrpc());
+ }
return Optional.ofNullable(response.getResult());
}
@NotNull
@Override
- public Optional txReceipt(final String txhash) throws ApiException {
+ public Optional txReceipt(@NotNull String txhash) throws EtherScanException {
BasicUtils.validateTxHash(txhash);
final String urlParams = ACT_TX_RECEIPT_PARAM + TXHASH_PARAM + txhash;
@@ -162,10 +170,10 @@ public Optional txReceipt(final String txhash) throws ApiException
@NotNull
@Override
- public Optional call(final String address, final String data) throws ApiException {
+ public Optional call(@NotNull String address, @NotNull String data) throws EtherScanException {
BasicUtils.validateAddress(address);
if (BasicUtils.isNotHex(data))
- throw new InvalidDataHexException("Data is not hex encoded.");
+ throw new EtherScanInvalidDataHexException("Data is not hex encoded.");
final String urlParams = ACT_CALL_PARAM + TO_PARAM + address + DATA_PARAM + data + TAG_LAST_PARAM;
final StringProxyTO response = getRequest(urlParams, StringProxyTO.class);
@@ -174,7 +182,7 @@ public Optional call(final String address, final String data) throws Api
@NotNull
@Override
- public Optional code(final String address) throws ApiException {
+ public Optional code(@NotNull String address) throws EtherScanException {
BasicUtils.validateAddress(address);
final String urlParams = ACT_CODE_PARAM + ADDRESS_PARAM + address + TAG_LAST_PARAM;
@@ -184,7 +192,7 @@ public Optional code(final String address) throws ApiException {
@NotNull
@Override
- public Optional storageAt(final String address, final long position) throws ApiException {
+ public Optional storageAt(@NotNull String address, long position) throws EtherScanException {
BasicUtils.validateAddress(address);
final long compPosition = BasicUtils.compensateMinBlock(position);
@@ -197,29 +205,29 @@ public Optional storageAt(final String address, final long position) thr
@NotNull
@Override
- public BigInteger gasPrice() throws ApiException {
+ public Wei gasPrice() throws EtherScanException {
final StringProxyTO response = getRequest(ACT_GASPRICE_PARAM, StringProxyTO.class);
return (BasicUtils.isEmpty(response.getResult()))
- ? BigInteger.valueOf(-1)
- : BasicUtils.parseHex(response.getResult());
+ ? Wei.ofWei(0)
+ : Wei.ofWei(BasicUtils.parseHex(response.getResult()));
}
@NotNull
@Override
- public BigInteger gasEstimated() throws ApiException {
+ public Wei gasEstimated() throws EtherScanException {
return gasEstimated("606060405260728060106000396000f360606040526000");
}
@NotNull
@Override
- public BigInteger gasEstimated(final String hexData) throws ApiException {
+ public Wei gasEstimated(@NotNull String hexData) throws EtherScanException {
if (!BasicUtils.isEmpty(hexData) && BasicUtils.isNotHex(hexData))
- throw new InvalidDataHexException("Data is not in hex format.");
+ throw new EtherScanInvalidDataHexException("Data is not in hex format.");
final String urlParams = ACT_ESTIMATEGAS_PARAM + DATA_PARAM + hexData + GAS_PARAM + "2000000000000000";
final StringProxyTO response = getRequest(urlParams, StringProxyTO.class);
return (BasicUtils.isEmpty(response.getResult()))
- ? BigInteger.valueOf(-1)
- : BasicUtils.parseHex(response.getResult());
+ ? Wei.ofWei(0)
+ : Wei.ofWei(BasicUtils.parseHex(response.getResult()));
}
}
diff --git a/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java b/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java
new file mode 100644
index 0000000..0a39eae
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java
@@ -0,0 +1,57 @@
+package io.goodforgod.api.etherscan;
+
+import io.goodforgod.api.etherscan.error.EtherScanException;
+import io.goodforgod.api.etherscan.model.EthSupply;
+import io.goodforgod.api.etherscan.model.Price;
+import io.goodforgod.api.etherscan.model.Wei;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * EtherScan - API Descriptions ...
+ *
+ * @author GoodforGod
+ * @since 30.10.2018
+ */
+public interface StatisticAPI {
+
+ /**
+ * ERC20 token total Supply
+ * EtherScan
+ *
+ * @param contract contract address
+ * @return token supply for specified contract
+ * @throws EtherScanException parent exception class
+ */
+ @NotNull
+ Wei supply(@NotNull String contract) throws EtherScanException;
+
+ /**
+ * Returns the current amount of Ether in circulation excluding ETH2 Staking rewards and EIP1559
+ * burnt fees.
+ *
+ * @return total ETH supply for moment
+ * @throws EtherScanException parent exception class
+ */
+ @NotNull
+ Wei supply() throws EtherScanException;
+
+ /**
+ * Returns the current amount of Ether in circulation, ETH2 Staking rewards, EIP1559 burnt fees, and
+ * total withdrawn ETH from the beacon chain.
+ *
+ * @return total ETH supply for moment
+ * @throws EtherScanException parent exception class
+ */
+ @NotNull
+ EthSupply supplyTotal() throws EtherScanException;
+
+ /**
+ * Eth last USD and BTC price
+ *
+ * @return last usd/btc price for ETH
+ * @throws EtherScanException parent exception class
+ */
+ @NotNull
+ Price priceLast() throws EtherScanException;
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java
new file mode 100644
index 0000000..131df71
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java
@@ -0,0 +1,81 @@
+package io.goodforgod.api.etherscan;
+
+import io.goodforgod.api.etherscan.error.EtherScanException;
+import io.goodforgod.api.etherscan.error.EtherScanResponseException;
+import io.goodforgod.api.etherscan.http.EthHttpClient;
+import io.goodforgod.api.etherscan.manager.RequestQueueManager;
+import io.goodforgod.api.etherscan.model.EthSupply;
+import io.goodforgod.api.etherscan.model.Price;
+import io.goodforgod.api.etherscan.model.Wei;
+import io.goodforgod.api.etherscan.model.response.EthSupplyResponseTO;
+import io.goodforgod.api.etherscan.model.response.PriceResponseTO;
+import io.goodforgod.api.etherscan.model.response.StringResponseTO;
+import io.goodforgod.api.etherscan.util.BasicUtils;
+import java.math.BigInteger;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Statistic API Implementation
+ *
+ * @see StatisticAPI
+ * @author GoodforGod
+ * @since 28.10.2018
+ */
+final class StatisticAPIProvider extends BasicProvider implements StatisticAPI {
+
+ private static final String ACT_SUPPLY_PARAM = ACT_PREFIX + "ethsupply";
+ private static final String ACT_SUPPLY2_PARAM = ACT_PREFIX + "ethsupply2";
+ private static final String ACT_TOKEN_SUPPLY_PARAM = ACT_PREFIX + "tokensupply";
+ private static final String ACT_LASTPRICE_PARAM = ACT_PREFIX + "ethprice";
+
+ private static final String CONTRACT_ADDRESS_PARAM = "&contractaddress=";
+
+ StatisticAPIProvider(RequestQueueManager queue,
+ String baseUrl,
+ EthHttpClient executor,
+ Converter converter) {
+ super(queue, "stats", baseUrl, executor, converter);
+ }
+
+ @NotNull
+ @Override
+ public Wei supply() throws EtherScanException {
+ final StringResponseTO response = getRequest(ACT_SUPPLY_PARAM, StringResponseTO.class);
+ if (response.getStatus() != 1)
+ throw new EtherScanResponseException(response);
+
+ return Wei.ofWei(new BigInteger(response.getResult()));
+ }
+
+ @Override
+ public @NotNull EthSupply supplyTotal() throws EtherScanException {
+ final EthSupplyResponseTO response = getRequest(ACT_SUPPLY2_PARAM, EthSupplyResponseTO.class);
+ if (response.getStatus() != 1)
+ throw new EtherScanResponseException(response);
+
+ return response.getResult();
+ }
+
+ @NotNull
+ @Override
+ public Wei supply(@NotNull String contract) throws EtherScanException {
+ BasicUtils.validateAddress(contract);
+
+ final String urlParams = ACT_TOKEN_SUPPLY_PARAM + CONTRACT_ADDRESS_PARAM + contract;
+ final StringResponseTO response = getRequest(urlParams, StringResponseTO.class);
+ if (response.getStatus() != 1)
+ throw new EtherScanResponseException(response);
+
+ return Wei.ofWei(new BigInteger(response.getResult()));
+ }
+
+ @NotNull
+ @Override
+ public Price priceLast() throws EtherScanException {
+ final PriceResponseTO response = getRequest(ACT_LASTPRICE_PARAM, PriceResponseTO.class);
+ if (response.getStatus() != 1)
+ throw new EtherScanResponseException(response);
+
+ return response.getResult();
+ }
+}
diff --git a/src/main/java/io/api/etherscan/core/ITransactionApi.java b/src/main/java/io/goodforgod/api/etherscan/TransactionAPI.java
similarity index 50%
rename from src/main/java/io/api/etherscan/core/ITransactionApi.java
rename to src/main/java/io/goodforgod/api/etherscan/TransactionAPI.java
index f545c2d..c719e5b 100644
--- a/src/main/java/io/api/etherscan/core/ITransactionApi.java
+++ b/src/main/java/io/goodforgod/api/etherscan/TransactionAPI.java
@@ -1,36 +1,35 @@
-package io.api.etherscan.core;
-
-import io.api.etherscan.error.ApiException;
-import io.api.etherscan.model.Status;
-import org.jetbrains.annotations.NotNull;
+package io.goodforgod.api.etherscan;
+import io.goodforgod.api.etherscan.error.EtherScanException;
+import io.goodforgod.api.etherscan.model.Status;
import java.util.Optional;
+import org.jetbrains.annotations.NotNull;
/**
- * EtherScan - API Descriptions https://etherscan.io/apis#transactions
+ * EtherScan - API Descriptions ...
*
* @author GoodforGod
* @since 30.10.2018
*/
-public interface ITransactionApi {
+public interface TransactionAPI {
/**
* Check Contract Execution Status (if there was an error during contract execution)
*
* @param txhash transaction hash
* @return optional status result
- * @throws ApiException parent exception class
+ * @throws EtherScanException parent exception class
*/
@NotNull
- Optional execStatus(String txhash) throws ApiException;
+ Optional statusExec(@NotNull String txhash) throws EtherScanException;
/**
* Check Transaction Receipt Status (Only applicable for Post Byzantium fork transactions)
*
* @param txhash transaction hash
* @return 0 = Fail, 1 = Pass, empty value for pre-byzantium fork
- * @throws ApiException parent exception class
+ * @throws EtherScanException parent exception class
*/
@NotNull
- Optional receiptStatus(String txhash) throws ApiException;
+ Optional statusReceipt(@NotNull String txhash) throws EtherScanException;
}
diff --git a/src/main/java/io/api/etherscan/core/impl/TransactionApiProvider.java b/src/main/java/io/goodforgod/api/etherscan/TransactionAPIProvider.java
similarity index 55%
rename from src/main/java/io/api/etherscan/core/impl/TransactionApiProvider.java
rename to src/main/java/io/goodforgod/api/etherscan/TransactionAPIProvider.java
index 82eb467..da26b51 100644
--- a/src/main/java/io/api/etherscan/core/impl/TransactionApiProvider.java
+++ b/src/main/java/io/goodforgod/api/etherscan/TransactionAPIProvider.java
@@ -1,40 +1,39 @@
-package io.api.etherscan.core.impl;
-
-import io.api.etherscan.core.ITransactionApi;
-import io.api.etherscan.error.ApiException;
-import io.api.etherscan.executor.IHttpExecutor;
-import io.api.etherscan.manager.IQueueManager;
-import io.api.etherscan.model.Status;
-import io.api.etherscan.model.utility.ReceiptStatusResponseTO;
-import io.api.etherscan.model.utility.StatusResponseTO;
-import io.api.etherscan.util.BasicUtils;
-import org.jetbrains.annotations.NotNull;
-
+package io.goodforgod.api.etherscan;
+
+import io.goodforgod.api.etherscan.error.EtherScanException;
+import io.goodforgod.api.etherscan.http.EthHttpClient;
+import io.goodforgod.api.etherscan.manager.RequestQueueManager;
+import io.goodforgod.api.etherscan.model.Status;
+import io.goodforgod.api.etherscan.model.response.ReceiptStatusResponseTO;
+import io.goodforgod.api.etherscan.model.response.StatusResponseTO;
+import io.goodforgod.api.etherscan.util.BasicUtils;
import java.util.Optional;
+import org.jetbrains.annotations.NotNull;
/**
* Transaction API Implementation
*
* @author GoodforGod
- * @see ITransactionApi
+ * @see TransactionAPI
* @since 28.10.2018
*/
-public class TransactionApiProvider extends BasicProvider implements ITransactionApi {
+final class TransactionAPIProvider extends BasicProvider implements TransactionAPI {
private static final String ACT_EXEC_STATUS_PARAM = ACT_PREFIX + "getstatus";
private static final String ACT_RECEIPT_STATUS_PARAM = ACT_PREFIX + "gettxreceiptstatus";
private static final String TXHASH_PARAM = "&txhash=";
- TransactionApiProvider(final IQueueManager queue,
- final String baseUrl,
- final IHttpExecutor executor) {
- super(queue, "transaction", baseUrl, executor);
+ TransactionAPIProvider(RequestQueueManager queue,
+ String baseUrl,
+ EthHttpClient executor,
+ Converter converter) {
+ super(queue, "transaction", baseUrl, executor, converter);
}
@NotNull
@Override
- public Optional execStatus(final String txhash) throws ApiException {
+ public Optional statusExec(@NotNull String txhash) throws EtherScanException {
BasicUtils.validateTxHash(txhash);
final String urlParams = ACT_EXEC_STATUS_PARAM + TXHASH_PARAM + txhash;
@@ -46,7 +45,7 @@ public Optional execStatus(final String txhash) throws ApiException {
@NotNull
@Override
- public Optional receiptStatus(final String txhash) throws ApiException {
+ public Optional statusReceipt(@NotNull String txhash) throws EtherScanException {
BasicUtils.validateTxHash(txhash);
final String urlParams = ACT_RECEIPT_STATUS_PARAM + TXHASH_PARAM + txhash;
diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanConnectionException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanConnectionException.java
new file mode 100644
index 0000000..731592c
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanConnectionException.java
@@ -0,0 +1,16 @@
+package io.goodforgod.api.etherscan.error;
+
+/**
+ * @author GoodforGod
+ * @since 29.10.2018
+ */
+public class EtherScanConnectionException extends EtherScanException {
+
+ public EtherScanConnectionException(String message) {
+ super(message);
+ }
+
+ public EtherScanConnectionException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanException.java
new file mode 100644
index 0000000..67cc3bb
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanException.java
@@ -0,0 +1,16 @@
+package io.goodforgod.api.etherscan.error;
+
+/**
+ * @author GoodforGod
+ * @since 30.10.2018
+ */
+public class EtherScanException extends RuntimeException {
+
+ public EtherScanException(String message) {
+ super(message);
+ }
+
+ public EtherScanException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidAddressException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidAddressException.java
new file mode 100644
index 0000000..ff12e3c
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidAddressException.java
@@ -0,0 +1,12 @@
+package io.goodforgod.api.etherscan.error;
+
+/**
+ * @author GoodforGod
+ * @since 29.10.2018
+ */
+public class EtherScanInvalidAddressException extends EtherScanException {
+
+ public EtherScanInvalidAddressException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidDataHexException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidDataHexException.java
new file mode 100644
index 0000000..b5b5f2d
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidDataHexException.java
@@ -0,0 +1,12 @@
+package io.goodforgod.api.etherscan.error;
+
+/**
+ * @author GoodforGod
+ * @since 02.11.2018
+ */
+public class EtherScanInvalidDataHexException extends EtherScanException {
+
+ public EtherScanInvalidDataHexException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidTxHashException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidTxHashException.java
new file mode 100644
index 0000000..33be4ad
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidTxHashException.java
@@ -0,0 +1,12 @@
+package io.goodforgod.api.etherscan.error;
+
+/**
+ * @author GoodforGod
+ * @since 02.11.2018
+ */
+public class EtherScanInvalidTxHashException extends EtherScanException {
+
+ public EtherScanInvalidTxHashException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanKeyException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanKeyException.java
new file mode 100644
index 0000000..f9e4aa8
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanKeyException.java
@@ -0,0 +1,12 @@
+package io.goodforgod.api.etherscan.error;
+
+/**
+ * @author GoodforGod
+ * @since 05.11.2018
+ */
+public class EtherScanKeyException extends EtherScanException {
+
+ public EtherScanKeyException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanLogQueryException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanLogQueryException.java
new file mode 100644
index 0000000..e72d682
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanLogQueryException.java
@@ -0,0 +1,12 @@
+package io.goodforgod.api.etherscan.error;
+
+/**
+ * @author GoodforGod
+ * @since 31.10.2018
+ */
+public class EtherScanLogQueryException extends EtherScanException {
+
+ public EtherScanLogQueryException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/io/api/etherscan/error/ParseException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanParseException.java
similarity index 52%
rename from src/main/java/io/api/etherscan/error/ParseException.java
rename to src/main/java/io/goodforgod/api/etherscan/error/EtherScanParseException.java
index 5dc6199..87116ab 100644
--- a/src/main/java/io/api/etherscan/error/ParseException.java
+++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanParseException.java
@@ -1,14 +1,14 @@
-package io.api.etherscan.error;
+package io.goodforgod.api.etherscan.error;
/**
* @author GoodforGod
* @since 29.10.2018
*/
-public class ParseException extends ApiException {
+public class EtherScanParseException extends EtherScanException {
private final String json;
- public ParseException(String message, Throwable cause, String json) {
+ public EtherScanParseException(String message, Throwable cause, String json) {
super(message, cause);
this.json = json;
}
diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanRateLimitException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanRateLimitException.java
new file mode 100644
index 0000000..eefb1ea
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanRateLimitException.java
@@ -0,0 +1,12 @@
+package io.goodforgod.api.etherscan.error;
+
+/**
+ * @author iSnow
+ * @since 2020-10-06
+ */
+public class EtherScanRateLimitException extends EtherScanException {
+
+ public EtherScanRateLimitException(String message) {
+ super(message);
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanResponseException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanResponseException.java
new file mode 100644
index 0000000..19785ce
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanResponseException.java
@@ -0,0 +1,31 @@
+package io.goodforgod.api.etherscan.error;
+
+import io.goodforgod.api.etherscan.model.response.BaseResponseTO;
+import io.goodforgod.api.etherscan.model.response.StringResponseTO;
+
+/**
+ * @author GoodforGod
+ * @since 29.10.2018
+ */
+public class EtherScanResponseException extends EtherScanException {
+
+ private final transient BaseResponseTO response;
+
+ public EtherScanResponseException(BaseResponseTO response) {
+ this(response, response.getMessage() + ", with status: " + response.getStatus());
+ }
+
+ public EtherScanResponseException(StringResponseTO response) {
+ this(response,
+ response.getResult() + ", with status: " + response.getStatus() + ", with message: " + response.getMessage());
+ }
+
+ public EtherScanResponseException(BaseResponseTO response, String message) {
+ super(message);
+ this.response = response;
+ }
+
+ public BaseResponseTO getResponse() {
+ return response;
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanTimeoutException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanTimeoutException.java
new file mode 100644
index 0000000..7734139
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanTimeoutException.java
@@ -0,0 +1,12 @@
+package io.goodforgod.api.etherscan.error;
+
+/**
+ * @author GoodforGod
+ * @since 12.11.2018
+ */
+public class EtherScanTimeoutException extends EtherScanConnectionException {
+
+ public EtherScanTimeoutException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/http/EthHttpClient.java b/src/main/java/io/goodforgod/api/etherscan/http/EthHttpClient.java
new file mode 100644
index 0000000..bd01f83
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/http/EthHttpClient.java
@@ -0,0 +1,30 @@
+package io.goodforgod.api.etherscan.http;
+
+import java.net.URI;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Http Client interface
+ *
+ * @author GoodforGod
+ * @since 31.10.2018
+ */
+public interface EthHttpClient {
+
+ /**
+ * Performs a Http GET request
+ *
+ * @param uri as string
+ * @return result as string
+ */
+ byte[] get(@NotNull URI uri);
+
+ /**
+ * Performs a Http POST request
+ *
+ * @param uri as string
+ * @param body to post
+ * @return result as string
+ */
+ byte[] post(@NotNull URI uri, byte[] body);
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/http/impl/UrlEthHttpClient.java b/src/main/java/io/goodforgod/api/etherscan/http/impl/UrlEthHttpClient.java
new file mode 100644
index 0000000..b298743
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/http/impl/UrlEthHttpClient.java
@@ -0,0 +1,163 @@
+package io.goodforgod.api.etherscan.http.impl;
+
+import static java.net.HttpURLConnection.*;
+
+import io.goodforgod.api.etherscan.error.EtherScanConnectionException;
+import io.goodforgod.api.etherscan.error.EtherScanTimeoutException;
+import io.goodforgod.api.etherscan.http.EthHttpClient;
+import java.io.*;
+import java.net.HttpURLConnection;
+import java.net.SocketTimeoutException;
+import java.net.URI;
+import java.net.URL;
+import java.time.Duration;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.InflaterInputStream;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Http client implementation
+ *
+ * @author GoodforGod
+ * @see EthHttpClient
+ * @since 28.10.2018
+ */
+public final class UrlEthHttpClient implements EthHttpClient {
+
+ private static final Map DEFAULT_HEADERS = new HashMap<>();
+
+ private static final Duration DEFAULT_CONNECT_TIMEOUT = Duration.ofSeconds(8);
+ private static final Duration DEFAULT_READ_TIMEOUT = Duration.ZERO;
+
+ static {
+ DEFAULT_HEADERS.put("Accept-Language", "en");
+ DEFAULT_HEADERS.put("Accept-Encoding", "deflate, gzip");
+ DEFAULT_HEADERS.put("User-Agent", "Chrome/68.0.3440.106");
+ DEFAULT_HEADERS.put("Accept-Charset", "UTF-8");
+ }
+
+ private final Map headers;
+ private final int connectTimeout;
+ private final int readTimeout;
+
+ public UrlEthHttpClient() {
+ this(DEFAULT_CONNECT_TIMEOUT);
+ }
+
+ public UrlEthHttpClient(Duration connectTimeout) {
+ this(connectTimeout, DEFAULT_READ_TIMEOUT);
+ }
+
+ public UrlEthHttpClient(Duration connectTimeout, Duration readTimeout) {
+ this(connectTimeout, readTimeout, DEFAULT_HEADERS);
+ }
+
+ /**
+ * @param connectTimeout custom connection establish timeout in millis
+ * @param readTimeout custom read timeout in millis
+ * @param headers custom HTTP headers
+ */
+ public UrlEthHttpClient(Duration connectTimeout,
+ Duration readTimeout,
+ Map headers) {
+ this.connectTimeout = Math.toIntExact(connectTimeout.toMillis());
+ this.readTimeout = Math.toIntExact(readTimeout.toMillis());
+ this.headers = Collections.unmodifiableMap(headers);
+ }
+
+ private HttpURLConnection buildConnection(URI uri, String method) throws IOException {
+ final URL url = uri.toURL();
+ final HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ connection.setRequestMethod(method);
+ connection.setConnectTimeout(connectTimeout);
+ connection.setReadTimeout(readTimeout);
+ headers.forEach(connection::setRequestProperty);
+ return connection;
+ }
+
+ @Override
+ public byte[] get(@NotNull URI uri) {
+ try {
+ final HttpURLConnection connection = buildConnection(uri, "GET");
+ final int status = connection.getResponseCode();
+ if (status == HTTP_MOVED_TEMP || status == HTTP_MOVED_PERM) {
+ return get(URI.create(connection.getHeaderField("Location")));
+ } else if ((status >= HTTP_BAD_REQUEST) && (status < HTTP_INTERNAL_ERROR)) {
+ throw new EtherScanConnectionException("Protocol error: " + connection.getResponseMessage());
+ } else if (status >= HTTP_INTERNAL_ERROR) {
+ throw new EtherScanConnectionException("Server error: " + connection.getResponseMessage());
+ }
+
+ final byte[] data = readData(connection);
+ connection.disconnect();
+ return data;
+ } catch (SocketTimeoutException e) {
+ throw new EtherScanTimeoutException("Timeout: Could not establish connection for " + connectTimeout + " millis", e);
+ } catch (Exception e) {
+ throw new EtherScanConnectionException(e.getMessage(), e);
+ }
+ }
+
+ @Override
+ public byte[] post(@NotNull URI uri, byte[] body) {
+ try {
+ final HttpURLConnection connection = buildConnection(uri, "POST");
+ final int contentLength = body.length;
+ connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
+ connection.setRequestProperty("Content-Length", String.valueOf(contentLength));
+ connection.setFixedLengthStreamingMode(body.length);
+
+ connection.setDoOutput(true);
+ connection.connect();
+ try (OutputStream os = connection.getOutputStream()) {
+ os.write(body);
+ }
+
+ final int status = connection.getResponseCode();
+ if (status == HTTP_MOVED_TEMP || status == HTTP_MOVED_PERM) {
+ return post(URI.create(connection.getHeaderField("Location")), body);
+ } else if ((status >= HTTP_BAD_REQUEST) && (status < HTTP_INTERNAL_ERROR)) {
+ throw new EtherScanConnectionException("Protocol error: " + connection.getResponseMessage());
+ } else if (status >= HTTP_INTERNAL_ERROR) {
+ throw new EtherScanConnectionException("Server error: " + connection.getResponseMessage());
+ }
+
+ final byte[] data = readData(connection);
+ connection.disconnect();
+ return data;
+ } catch (SocketTimeoutException e) {
+ throw new EtherScanTimeoutException("Timeout: Could not establish connection for " + connectTimeout + " millis", e);
+ } catch (Exception e) {
+ throw new EtherScanConnectionException(e.getMessage(), e);
+ }
+ }
+
+ private byte[] readData(HttpURLConnection connection) throws IOException {
+ try (ByteArrayOutputStream buffer = new ByteArrayOutputStream()) {
+ try (InputStream in = getStreamReader(connection)) {
+ byte[] data = new byte[256];
+ int nRead;
+ while ((nRead = in.read(data, 0, data.length)) != -1) {
+ buffer.write(data, 0, nRead);
+ }
+ }
+
+ buffer.flush();
+ return buffer.toByteArray();
+ }
+ }
+
+ private InputStream getStreamReader(HttpURLConnection connection) throws IOException {
+ switch (String.valueOf(connection.getContentEncoding())) {
+ case "gzip":
+ return new GZIPInputStream(connection.getInputStream());
+ case "deflate":
+ return new InflaterInputStream(connection.getInputStream());
+ default:
+ return connection.getInputStream();
+ }
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java b/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java
new file mode 100644
index 0000000..0f36b23
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java
@@ -0,0 +1,51 @@
+package io.goodforgod.api.etherscan.manager;
+
+import io.goodforgod.api.etherscan.manager.impl.FakeRequestQueueManager;
+import io.goodforgod.api.etherscan.manager.impl.SemaphoreRequestQueueManager;
+import java.time.Duration;
+
+/**
+ * Queue manager to support API limits
+ * Manager grants turn if the limit is not exhausted And resets queue each set period
+ *
+ * @author GoodforGod
+ * @since 30.10.2018
+ */
+public interface RequestQueueManager extends AutoCloseable {
+
+ /**
+ * Is used by default when no API KEY is provided
+ */
+ static RequestQueueManager anonymous() {
+ return new SemaphoreRequestQueueManager(1, Duration.ofMillis(5015L));
+ }
+
+ /**
+ * Is available for all registered free API KEYs
+ * Free API KEY
+ */
+ static RequestQueueManager planFree() {
+ return new SemaphoreRequestQueueManager(5, Duration.ofMillis(1015L));
+ }
+
+ static RequestQueueManager planStandard() {
+ return new SemaphoreRequestQueueManager(10, Duration.ofMillis(1015L));
+ }
+
+ static RequestQueueManager planAdvanced() {
+ return new SemaphoreRequestQueueManager(20, Duration.ofMillis(1015L));
+ }
+
+ static RequestQueueManager planProfessional() {
+ return new SemaphoreRequestQueueManager(30, Duration.ofMillis(1015L));
+ }
+
+ static RequestQueueManager unlimited() {
+ return new FakeRequestQueueManager();
+ }
+
+ /**
+ * Waits in queue for chance to take turn
+ */
+ void takeTurn();
+}
diff --git a/src/main/java/io/api/etherscan/manager/impl/FakeQueueManager.java b/src/main/java/io/goodforgod/api/etherscan/manager/impl/FakeRequestQueueManager.java
similarity index 65%
rename from src/main/java/io/api/etherscan/manager/impl/FakeQueueManager.java
rename to src/main/java/io/goodforgod/api/etherscan/manager/impl/FakeRequestQueueManager.java
index 620244c..626b4c1 100644
--- a/src/main/java/io/api/etherscan/manager/impl/FakeQueueManager.java
+++ b/src/main/java/io/goodforgod/api/etherscan/manager/impl/FakeRequestQueueManager.java
@@ -1,6 +1,6 @@
-package io.api.etherscan.manager.impl;
+package io.goodforgod.api.etherscan.manager.impl;
-import io.api.etherscan.manager.IQueueManager;
+import io.goodforgod.api.etherscan.manager.RequestQueueManager;
/**
* Fake queue manager, always give turns, when you have no limits
@@ -8,7 +8,7 @@
* @author GoodforGod
* @since 03.11.2018
*/
-public class FakeQueueManager implements IQueueManager {
+public final class FakeRequestQueueManager implements RequestQueueManager {
@Override
public void takeTurn() {
diff --git a/src/main/java/io/goodforgod/api/etherscan/manager/impl/SemaphoreRequestQueueManager.java b/src/main/java/io/goodforgod/api/etherscan/manager/impl/SemaphoreRequestQueueManager.java
new file mode 100644
index 0000000..2a3483c
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/manager/impl/SemaphoreRequestQueueManager.java
@@ -0,0 +1,54 @@
+package io.goodforgod.api.etherscan.manager.impl;
+
+import io.goodforgod.api.etherscan.manager.RequestQueueManager;
+import java.time.Duration;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Queue Semaphore implementation with size and reset time as params
+ *
+ * @see RequestQueueManager
+ * @author GoodforGod
+ * @since 30.10.2018
+ */
+public final class SemaphoreRequestQueueManager implements RequestQueueManager, AutoCloseable {
+
+ private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
+ private final Semaphore semaphore;
+ private final long queueResetTimeInMillis;
+
+ public SemaphoreRequestQueueManager(int size, Duration resetIn) {
+ this.semaphore = new Semaphore(0);
+ this.queueResetTimeInMillis = resetIn.toMillis();
+ this.executorService.scheduleAtFixedRate(releaseLocks(size),
+ resetIn.toMillis(), queueResetTimeInMillis, TimeUnit.MILLISECONDS);
+ }
+
+ @SuppressWarnings("java:S899")
+ @Override
+ public void takeTurn() {
+ try {
+ semaphore.tryAcquire(queueResetTimeInMillis, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ private Runnable releaseLocks(int toRelease) {
+ return () -> {
+ int availablePermits = semaphore.availablePermits();
+ int neededPermits = toRelease - availablePermits;
+ if (neededPermits > 0) {
+ semaphore.release(neededPermits);
+ }
+ };
+ }
+
+ @Override
+ public void close() {
+ executorService.shutdown();
+ }
+}
diff --git a/src/main/java/io/api/etherscan/model/Abi.java b/src/main/java/io/goodforgod/api/etherscan/model/Abi.java
similarity index 51%
rename from src/main/java/io/api/etherscan/model/Abi.java
rename to src/main/java/io/goodforgod/api/etherscan/model/Abi.java
index a48a11d..3536bf9 100644
--- a/src/main/java/io/api/etherscan/model/Abi.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/Abi.java
@@ -1,17 +1,16 @@
-package io.api.etherscan.model;
+package io.goodforgod.api.etherscan.model;
-import io.api.etherscan.util.BasicUtils;
+import io.goodforgod.api.etherscan.util.BasicUtils;
+import java.util.Objects;
/**
- * ! NO DESCRIPTION !
- *
* @author GoodforGod
* @since 31.10.2018
*/
public class Abi {
- private String contractAbi;
- private boolean isVerified;
+ private final String contractAbi;
+ private final boolean isVerified;
private Abi(String contractAbi, boolean isVerified) {
this.contractAbi = contractAbi;
@@ -42,21 +41,15 @@ public boolean isVerified() {
public boolean equals(Object o) {
if (this == o)
return true;
- if (o == null || getClass() != o.getClass())
+ if (!(o instanceof Abi))
return false;
-
Abi abi = (Abi) o;
-
- if (isVerified != abi.isVerified)
- return false;
- return contractAbi != null ? contractAbi.equals(abi.contractAbi) : abi.contractAbi == null;
+ return isVerified == abi.isVerified && Objects.equals(contractAbi, abi.contractAbi);
}
@Override
public int hashCode() {
- int result = contractAbi != null ? contractAbi.hashCode() : 0;
- result = 31 * result + (isVerified ? 1 : 0);
- return result;
+ return Objects.hash(contractAbi, isVerified);
}
@Override
@@ -66,4 +59,30 @@ public String toString() {
", isVerified=" + isVerified +
'}';
}
+
+ public static AbiBuilder builder() {
+ return new AbiBuilder();
+ }
+
+ public static final class AbiBuilder {
+
+ private String contractAbi;
+ private boolean isVerified;
+
+ private AbiBuilder() {}
+
+ public AbiBuilder withContractAbi(String contractAbi) {
+ this.contractAbi = contractAbi;
+ return this;
+ }
+
+ public AbiBuilder withIsVerified(boolean isVerified) {
+ this.isVerified = isVerified;
+ return this;
+ }
+
+ public Abi build() {
+ return new Abi(contractAbi, isVerified);
+ }
+ }
}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Balance.java b/src/main/java/io/goodforgod/api/etherscan/model/Balance.java
new file mode 100644
index 0000000..079d4b6
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/Balance.java
@@ -0,0 +1,52 @@
+package io.goodforgod.api.etherscan.model;
+
+import java.util.Objects;
+
+/**
+ * @author GoodforGod
+ * @since 28.10.2018
+ */
+public class Balance {
+
+ /** Balance in Wei */
+ private final Wei balance;
+ private final String address;
+
+ public Balance(String address, Wei balance) {
+ this.address = address;
+ this.balance = balance;
+ }
+
+ //
+ public String getAddress() {
+ return address;
+ }
+
+ public Wei getBalanceInWei() {
+ return balance;
+ }
+ //
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (!(o instanceof Balance))
+ return false;
+ Balance balance1 = (Balance) o;
+ return Objects.equals(balance, balance1.balance) && Objects.equals(address, balance1.address);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(balance, address);
+ }
+
+ @Override
+ public String toString() {
+ return "Balance{" +
+ "address='" + address + '\'' +
+ ", balance=" + balance +
+ '}';
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/BaseTx.java b/src/main/java/io/goodforgod/api/etherscan/model/BaseTx.java
new file mode 100644
index 0000000..8de679a
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/BaseTx.java
@@ -0,0 +1,91 @@
+package io.goodforgod.api.etherscan.model;
+
+import com.google.gson.annotations.Expose;
+import io.goodforgod.api.etherscan.util.BasicUtils;
+import java.math.BigInteger;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.Objects;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author GoodforGod
+ * @since 28.10.2018
+ */
+abstract class BaseTx implements Comparable {
+
+ long blockNumber;
+ String timeStamp;
+ @Expose(deserialize = false, serialize = false)
+ LocalDateTime _timeStamp;
+ String hash;
+ String from;
+ String to;
+ String contractAddress;
+ String input;
+ BigInteger gas;
+ BigInteger gasUsed;
+
+ //
+ public long getBlockNumber() {
+ return blockNumber;
+ }
+
+ public LocalDateTime getTimeStamp() {
+ if (_timeStamp == null && !BasicUtils.isEmpty(timeStamp))
+ _timeStamp = LocalDateTime.ofEpochSecond(Long.parseLong(timeStamp), 0, ZoneOffset.UTC);
+ return _timeStamp;
+ }
+
+ public String getHash() {
+ return hash;
+ }
+
+ public String getFrom() {
+ return from;
+ }
+
+ public String getTo() {
+ return to;
+ }
+
+ public String getContractAddress() {
+ return contractAddress;
+ }
+
+ public String getInput() {
+ return input;
+ }
+
+ public Wei getGas() {
+ return Wei.ofWei(gas);
+ }
+
+ public Wei getGasUsed() {
+ return Wei.ofWei(gasUsed);
+ }
+ //
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (!(o instanceof BaseTx))
+ return false;
+ BaseTx baseTx = (BaseTx) o;
+ return blockNumber == baseTx.blockNumber && Objects.equals(timeStamp, baseTx.timeStamp)
+ && Objects.equals(hash, baseTx.hash) && Objects.equals(from, baseTx.from) && Objects.equals(to, baseTx.to)
+ && Objects.equals(contractAddress, baseTx.contractAddress) && Objects.equals(input, baseTx.input)
+ && Objects.equals(gas, baseTx.gas) && Objects.equals(gasUsed, baseTx.gasUsed);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(blockNumber, timeStamp, hash, from, to, contractAddress, input, gas, gasUsed);
+ }
+
+ @Override
+ public int compareTo(@NotNull BaseTx o) {
+ return Long.compare(blockNumber, o.blockNumber);
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Block.java b/src/main/java/io/goodforgod/api/etherscan/model/Block.java
new file mode 100644
index 0000000..0550000
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/Block.java
@@ -0,0 +1,108 @@
+package io.goodforgod.api.etherscan.model;
+
+import com.google.gson.annotations.Expose;
+import io.goodforgod.api.etherscan.util.BasicUtils;
+import java.math.BigInteger;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.Objects;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author GoodforGod
+ * @since 28.10.2018
+ */
+public class Block implements Comparable {
+
+ long blockNumber;
+ BigInteger blockReward;
+ String timeStamp;
+ @Expose(deserialize = false, serialize = false)
+ LocalDateTime _timeStamp;
+
+ protected Block() {}
+
+ //
+ public long getBlockNumber() {
+ return blockNumber;
+ }
+
+ public LocalDateTime getTimeStamp() {
+ if (_timeStamp == null && !BasicUtils.isEmpty(timeStamp))
+ _timeStamp = LocalDateTime.ofEpochSecond(Long.parseLong(timeStamp), 0, ZoneOffset.UTC);
+ return _timeStamp;
+ }
+
+ public BigInteger getBlockReward() {
+ return blockReward;
+ }
+ //
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (!(o instanceof Block))
+ return false;
+ Block block = (Block) o;
+ return blockNumber == block.blockNumber;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(blockNumber);
+ }
+
+ @Override
+ public String toString() {
+ return "Block{" +
+ "blockNumber=" + blockNumber +
+ ", blockReward=" + blockReward +
+ ", timeStamp='" + timeStamp + '\'' +
+ '}';
+ }
+
+ @Override
+ public int compareTo(@NotNull Block o) {
+ return Long.compare(blockNumber, o.blockNumber);
+ }
+
+ public static BlockBuilder builder() {
+ return new BlockBuilder();
+ }
+
+ public static class BlockBuilder {
+
+ private long blockNumber;
+ private BigInteger blockReward;
+ private LocalDateTime timeStamp;
+
+ BlockBuilder() {}
+
+ public BlockBuilder withBlockNumber(long blockNumber) {
+ this.blockNumber = blockNumber;
+ return this;
+ }
+
+ public BlockBuilder withBlockReward(BigInteger blockReward) {
+ this.blockReward = blockReward;
+ return this;
+ }
+
+ public BlockBuilder withTimeStamp(LocalDateTime timeStamp) {
+ this.timeStamp = timeStamp;
+ return this;
+ }
+
+ public Block build() {
+ Block block = new Block();
+ block.blockNumber = this.blockNumber;
+ block.blockReward = this.blockReward;
+ if (this.timeStamp != null) {
+ block._timeStamp = this.timeStamp;
+ block.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC));
+ }
+ return block;
+ }
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/BlockTx.java b/src/main/java/io/goodforgod/api/etherscan/model/BlockTx.java
new file mode 100644
index 0000000..f3c4d67
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/BlockTx.java
@@ -0,0 +1,79 @@
+package io.goodforgod.api.etherscan.model;
+
+import java.math.BigInteger;
+import java.util.Objects;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Anton Kurako (GoodforGod)
+ * @since 15.05.2023
+ */
+abstract class BlockTx extends BaseTx {
+
+ long nonce;
+ String blockHash;
+ long transactionIndex;
+ long confirmations;
+ BigInteger gasPrice;
+ BigInteger cumulativeGasUsed;
+
+ public long getNonce() {
+ return nonce;
+ }
+
+ public String getBlockHash() {
+ return blockHash;
+ }
+
+ public long getTransactionIndex() {
+ return transactionIndex;
+ }
+
+ public Wei getGasPrice() {
+ return Wei.ofWei(gasPrice);
+ }
+
+ public Wei getGasUsedCumulative() {
+ return Wei.ofWei(cumulativeGasUsed);
+ }
+
+ public long getConfirmations() {
+ return confirmations;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (!(o instanceof BlockTx))
+ return false;
+ if (!super.equals(o))
+ return false;
+ BlockTx blockTx = (BlockTx) o;
+ return nonce == blockTx.nonce && transactionIndex == blockTx.transactionIndex
+ && Objects.equals(blockHash, blockTx.blockHash);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), nonce, blockHash, transactionIndex);
+ }
+
+ @Override
+ public int compareTo(@NotNull BaseTx o) {
+ final int superCompare = super.compareTo(o);
+ if (superCompare == 0) {
+ if (o instanceof Tx) {
+ return Long.compare(transactionIndex, ((Tx) o).getTransactionIndex());
+ } else if (o instanceof TxErc20) {
+ return Long.compare(transactionIndex, ((TxErc20) o).getTransactionIndex());
+ } else if (o instanceof TxErc721) {
+ return Long.compare(transactionIndex, ((TxErc721) o).getTransactionIndex());
+ } else if (o instanceof TxErc1155) {
+ return Long.compare(transactionIndex, ((TxErc1155) o).getTransactionIndex());
+ }
+ }
+
+ return superCompare;
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java b/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java
new file mode 100644
index 0000000..9b110d9
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java
@@ -0,0 +1,198 @@
+package io.goodforgod.api.etherscan.model;
+
+import io.goodforgod.api.etherscan.util.BasicUtils;
+import java.math.BigInteger;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * @author GoodforGod
+ * @since 30.10.2018
+ */
+public class BlockUncle extends Block {
+
+ public static class Uncle {
+
+ private String miner;
+ private BigInteger blockreward;
+ private int unclePosition;
+
+ private Uncle() {}
+
+ //
+ public String getMiner() {
+ return miner;
+ }
+
+ public BigInteger getBlockreward() {
+ return blockreward;
+ }
+
+ public int getUnclePosition() {
+ return unclePosition;
+ }
+ //
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (!(o instanceof Uncle))
+ return false;
+ Uncle uncle = (Uncle) o;
+ return unclePosition == uncle.unclePosition && Objects.equals(miner, uncle.miner)
+ && Objects.equals(blockreward, uncle.blockreward);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(miner, blockreward, unclePosition);
+ }
+
+ @Override
+ public String toString() {
+ return "Uncle{" +
+ "miner='" + miner + '\'' +
+ ", blockreward=" + blockreward +
+ ", unclePosition=" + unclePosition +
+ '}';
+ }
+
+ public static UncleBuilder builder() {
+ return new UncleBuilder();
+ }
+
+ public static final class UncleBuilder {
+
+ private String miner;
+ private BigInteger blockreward;
+ private int unclePosition;
+
+ private UncleBuilder() {}
+
+ public UncleBuilder withMiner(String miner) {
+ this.miner = miner;
+ return this;
+ }
+
+ public UncleBuilder withBlockreward(BigInteger blockreward) {
+ this.blockreward = blockreward;
+ return this;
+ }
+
+ public UncleBuilder withUnclePosition(int unclePosition) {
+ this.unclePosition = unclePosition;
+ return this;
+ }
+
+ public Uncle build() {
+ Uncle uncle = new Uncle();
+ uncle.miner = this.miner;
+ uncle.blockreward = this.blockreward;
+ uncle.unclePosition = this.unclePosition;
+ return uncle;
+ }
+ }
+ }
+
+ private String blockMiner;
+ private List uncles;
+ private String uncleInclusionReward;
+
+ protected BlockUncle() {
+ super();
+ }
+
+ //
+ public boolean isEmpty() {
+ return getBlockNumber() == 0 && getBlockReward() == null
+ && getTimeStamp() == null
+ && BasicUtils.isEmpty(blockMiner);
+ }
+
+ public String getBlockMiner() {
+ return blockMiner;
+ }
+
+ public List getUncles() {
+ return uncles;
+ }
+
+ public String getUncleInclusionReward() {
+ return uncleInclusionReward;
+ }
+ //
+
+ @Override
+ public String toString() {
+ return "UncleBlock{" +
+ "blockMiner='" + blockMiner + '\'' +
+ ", uncles=" + uncles +
+ ", uncleInclusionReward='" + uncleInclusionReward + '\'' +
+ '}';
+ }
+
+ public static BlockUncleBuilder builder() {
+ return new BlockUncleBuilder();
+ }
+
+ public static final class BlockUncleBuilder extends Block.BlockBuilder {
+
+ private long blockNumber;
+ private BigInteger blockReward;
+ private LocalDateTime timeStamp;
+ private String blockMiner;
+ private List uncles;
+ private String uncleInclusionReward;
+
+ private BlockUncleBuilder() {
+ super();
+ }
+
+ public BlockUncleBuilder withBlockNumber(long blockNumber) {
+ this.blockNumber = blockNumber;
+ return this;
+ }
+
+ public BlockUncleBuilder withBlockReward(BigInteger blockReward) {
+ this.blockReward = blockReward;
+ return this;
+ }
+
+ public BlockUncleBuilder withTimeStamp(LocalDateTime timeStamp) {
+ this.timeStamp = timeStamp;
+ return this;
+ }
+
+ public BlockUncleBuilder withBlockMiner(String blockMiner) {
+ this.blockMiner = blockMiner;
+ return this;
+ }
+
+ public BlockUncleBuilder withUncles(List uncles) {
+ this.uncles = uncles;
+ return this;
+ }
+
+ public BlockUncleBuilder withUncleInclusionReward(String uncleInclusionReward) {
+ this.uncleInclusionReward = uncleInclusionReward;
+ return this;
+ }
+
+ public BlockUncle build() {
+ BlockUncle blockUncle = new BlockUncle();
+ blockUncle.uncles = this.uncles;
+ blockUncle.uncleInclusionReward = this.uncleInclusionReward;
+ blockUncle.blockNumber = this.blockNumber;
+ blockUncle.blockReward = this.blockReward;
+ blockUncle.blockMiner = this.blockMiner;
+ if (this.timeStamp != null) {
+ blockUncle._timeStamp = this.timeStamp;
+ blockUncle.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC));
+ }
+ return blockUncle;
+ }
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/EthSupply.java b/src/main/java/io/goodforgod/api/etherscan/model/EthSupply.java
new file mode 100644
index 0000000..344e754
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/EthSupply.java
@@ -0,0 +1,124 @@
+package io.goodforgod.api.etherscan.model;
+
+import java.math.BigInteger;
+import java.util.Objects;
+
+/**
+ * @author Anton Kurako (GoodforGod)
+ * @since 14.05.2023
+ */
+public class EthSupply {
+
+ private String EthSupply;
+ private String Eth2Staking;
+ private String BurntFees;
+ private String WithdrawnTotal;
+
+ public Wei getEthSupply() {
+ return Wei.ofWei(new BigInteger(EthSupply));
+ }
+
+ public Wei getEth2Staking() {
+ return Wei.ofWei(new BigInteger(Eth2Staking));
+ }
+
+ public Wei getBurntFees() {
+ return Wei.ofWei(new BigInteger(BurntFees));
+ }
+
+ public Wei getTotal() {
+ final BigInteger total = getEthSupply().asWei()
+ .add(getEth2Staking().asWei())
+ .min(getBurntFees().asWei());
+ return Wei.ofWei(total);
+ }
+
+ public Wei getWithdrawnTotal() {
+ return Wei.ofWei(new BigInteger(WithdrawnTotal));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (!(o instanceof EthSupply))
+ return false;
+ EthSupply ethSupply = (EthSupply) o;
+ return Objects.equals(EthSupply, ethSupply.EthSupply) && Objects.equals(Eth2Staking, ethSupply.Eth2Staking)
+ && Objects.equals(BurntFees, ethSupply.BurntFees) && Objects.equals(WithdrawnTotal, ethSupply.WithdrawnTotal);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(EthSupply, Eth2Staking, BurntFees, WithdrawnTotal);
+ }
+
+ @Override
+ public String toString() {
+ return "EthSupply{" +
+ "EthSupply='" + EthSupply + '\'' +
+ ", Eth2Staking='" + Eth2Staking + '\'' +
+ ", BurntFees='" + BurntFees + '\'' +
+ ", WithdrawnTotal='" + WithdrawnTotal + '\'' +
+ '}';
+ }
+
+ public static EthSupplyBuilder builder() {
+ return new EthSupplyBuilder();
+ }
+
+ public static final class EthSupplyBuilder {
+
+ private Wei ethSupply;
+ private Wei eth2Staking;
+ private Wei burntFees;
+ private Wei withdrawnTotal;
+
+ private EthSupplyBuilder() {}
+
+ public EthSupplyBuilder withEthSupply(Wei ethSupply) {
+ this.ethSupply = ethSupply;
+ return this;
+ }
+
+ public EthSupplyBuilder withEth2Staking(Wei eth2Staking) {
+ this.eth2Staking = eth2Staking;
+ return this;
+ }
+
+ public EthSupplyBuilder withBurntFees(Wei burntFees) {
+ this.burntFees = burntFees;
+ return this;
+ }
+
+ public EthSupplyBuilder withWithdrawnTotal(Wei withdrawnTotal) {
+ this.withdrawnTotal = withdrawnTotal;
+ return this;
+ }
+
+ public EthSupply build() {
+ EthSupply ethSupply = new EthSupply();
+ if (this.burntFees != null) {
+ ethSupply.BurntFees = this.burntFees.toString();
+ } else {
+ ethSupply.BurntFees = BigInteger.ZERO.toString();
+ }
+ if (this.eth2Staking != null) {
+ ethSupply.Eth2Staking = this.eth2Staking.toString();
+ } else {
+ ethSupply.Eth2Staking = BigInteger.ZERO.toString();
+ }
+ if (this.ethSupply != null) {
+ ethSupply.EthSupply = this.ethSupply.toString();
+ } else {
+ ethSupply.EthSupply = BigInteger.ZERO.toString();
+ }
+ if (this.withdrawnTotal != null) {
+ ethSupply.WithdrawnTotal = this.withdrawnTotal.toString();
+ } else {
+ ethSupply.WithdrawnTotal = BigInteger.ZERO.toString();
+ }
+ return ethSupply;
+ }
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java b/src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java
new file mode 100644
index 0000000..6fe1231
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java
@@ -0,0 +1,148 @@
+package io.goodforgod.api.etherscan.model;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+/**
+ * @author Abhay Gupta
+ * @since 14.11.2022
+ */
+public class GasOracle {
+
+ private Long LastBlock;
+ private BigInteger SafeGasPrice;
+ private BigInteger ProposeGasPrice;
+ private BigInteger FastGasPrice;
+ private BigDecimal suggestBaseFee;
+ private String gasUsedRatio;
+
+ protected GasOracle() {}
+
+ public Long getLastBlock() {
+ return LastBlock;
+ }
+
+ public Wei getSafeGasPriceInWei() {
+ return Wei.ofGwei(SafeGasPrice);
+ }
+
+ public Wei getProposeGasPriceInWei() {
+ return Wei.ofGwei(ProposeGasPrice);
+ }
+
+ public Wei getFastGasPriceInWei() {
+ return Wei.ofGwei(FastGasPrice);
+ }
+
+ public BigDecimal getSuggestBaseFee() {
+ return suggestBaseFee;
+ }
+
+ public List getGasUsedRatio() {
+ return Arrays.stream(gasUsedRatio.split(","))
+ .map(BigDecimal::new)
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (!(o instanceof GasOracle))
+ return false;
+ GasOracle gasOracle = (GasOracle) o;
+ return Objects.equals(LastBlock, gasOracle.LastBlock) && Objects.equals(SafeGasPrice, gasOracle.SafeGasPrice)
+ && Objects.equals(ProposeGasPrice, gasOracle.ProposeGasPrice)
+ && Objects.equals(FastGasPrice, gasOracle.FastGasPrice)
+ && Objects.equals(suggestBaseFee, gasOracle.suggestBaseFee)
+ && Objects.equals(gasUsedRatio, gasOracle.gasUsedRatio);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(LastBlock, SafeGasPrice, ProposeGasPrice, FastGasPrice, suggestBaseFee, gasUsedRatio);
+ }
+
+ @Override
+ public String toString() {
+ return "GasOracle{" +
+ "LastBlock=" + LastBlock +
+ ", SafeGasPrice=" + SafeGasPrice +
+ ", ProposeGasPrice=" + ProposeGasPrice +
+ ", FastGasPrice=" + FastGasPrice +
+ ", suggestBaseFee=" + suggestBaseFee +
+ ", gasUsedRatio=" + gasUsedRatio +
+ '}';
+ }
+
+ public static GasOracleBuilder builder() {
+ return new GasOracleBuilder();
+ }
+
+ public static final class GasOracleBuilder {
+
+ private Long lastBlock;
+ private Wei safeGasPrice;
+ private Wei proposeGasPrice;
+ private Wei fastGasPrice;
+ private BigDecimal suggestBaseFee;
+ private List gasUsedRatio;
+
+ private GasOracleBuilder() {}
+
+ public GasOracleBuilder withLastBlock(Long lastBlock) {
+ this.lastBlock = lastBlock;
+ return this;
+ }
+
+ public GasOracleBuilder withSafeGasPrice(Wei safeGasPrice) {
+ this.safeGasPrice = safeGasPrice;
+ return this;
+ }
+
+ public GasOracleBuilder withProposeGasPrice(Wei proposeGasPrice) {
+ this.proposeGasPrice = proposeGasPrice;
+ return this;
+ }
+
+ public GasOracleBuilder withFastGasPrice(Wei fastGasPrice) {
+ this.fastGasPrice = fastGasPrice;
+ return this;
+ }
+
+ public GasOracleBuilder withSuggestBaseFee(BigDecimal suggestBaseFee) {
+ this.suggestBaseFee = suggestBaseFee;
+ return this;
+ }
+
+ public GasOracleBuilder withGasUsedRatio(List gasUsedRatio) {
+ this.gasUsedRatio = gasUsedRatio;
+ return this;
+ }
+
+ public GasOracle build() {
+ GasOracle gasOracle = new GasOracle();
+ gasOracle.LastBlock = this.lastBlock;
+ gasOracle.suggestBaseFee = this.suggestBaseFee;
+ if (this.proposeGasPrice != null) {
+ gasOracle.ProposeGasPrice = this.proposeGasPrice.asGwei().toBigInteger();
+ }
+ if (this.safeGasPrice != null) {
+ gasOracle.SafeGasPrice = this.safeGasPrice.asGwei().toBigInteger();
+ }
+ if (this.fastGasPrice != null) {
+ gasOracle.FastGasPrice = this.fastGasPrice.asGwei().toBigInteger();
+ }
+ if (this.gasUsedRatio != null) {
+ gasOracle.gasUsedRatio = this.gasUsedRatio.stream()
+ .map(BigDecimal::toString)
+ .collect(Collectors.joining(","));
+ }
+ return gasOracle;
+ }
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Log.java b/src/main/java/io/goodforgod/api/etherscan/model/Log.java
new file mode 100644
index 0000000..da6c295
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/Log.java
@@ -0,0 +1,248 @@
+package io.goodforgod.api.etherscan.model;
+
+import com.google.gson.annotations.Expose;
+import io.goodforgod.api.etherscan.util.BasicUtils;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * @author GoodforGod
+ * @since 31.10.2018
+ */
+public class Log {
+
+ private String blockNumber;
+ @Expose(deserialize = false, serialize = false)
+ private Long _blockNumber;
+ private String address;
+ private String transactionHash;
+ private String transactionIndex;
+ @Expose(deserialize = false, serialize = false)
+ private Long _transactionIndex;
+ private String timeStamp;
+ @Expose(deserialize = false, serialize = false)
+ private LocalDateTime _timeStamp;
+ private String data;
+ private String gasPrice;
+ @Expose(deserialize = false, serialize = false)
+ private Wei _gasPrice;
+ private String gasUsed;
+ @Expose(deserialize = false, serialize = false)
+ private Wei _gasUsed;
+ private List topics;
+ private String logIndex;
+ @Expose(deserialize = false, serialize = false)
+ private Long _logIndex;
+
+ protected Log() {}
+
+ //
+ public Long getBlockNumber() {
+ if (_blockNumber == null && !BasicUtils.isEmpty(blockNumber)) {
+ _blockNumber = BasicUtils.parseHex(blockNumber).longValue();
+ }
+ return _blockNumber;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ public String getTransactionHash() {
+ return transactionHash;
+ }
+
+ public Long getTransactionIndex() {
+ if (_transactionIndex == null && !BasicUtils.isEmpty(transactionIndex)) {
+ _transactionIndex = BasicUtils.parseHex(transactionIndex).longValue();
+ }
+
+ return _transactionIndex;
+ }
+
+ public LocalDateTime getTimeStamp() {
+ if (_timeStamp == null && !BasicUtils.isEmpty(timeStamp)) {
+ long formatted = getTimeStampAsSeconds();
+ _timeStamp = LocalDateTime.ofEpochSecond(formatted, 0, ZoneOffset.UTC);
+ }
+ return _timeStamp;
+ }
+
+ /**
+ * Return the "timeStamp" field of the event record as a long-int representing the seconds
+ * since the Unix epoch (1970-01-01 00:00:00).
+ *
+ * @return milliseconds between Unix epoch and `timeStamp`. If field is empty or null, returns null
+ */
+ public Long getTimeStampAsSeconds() {
+ if (BasicUtils.isEmpty(timeStamp)) {
+ return null;
+ }
+
+ return (timeStamp.charAt(0) == '0' && timeStamp.charAt(1) == 'x')
+ ? BasicUtils.parseHex(timeStamp).longValue()
+ : Long.parseLong(timeStamp);
+ }
+
+ public String getData() {
+ return data;
+ }
+
+ public Wei getGasPrice() {
+ if (!BasicUtils.isEmpty(gasPrice)) {
+ _gasPrice = Wei.ofWei(BasicUtils.parseHex(gasPrice));
+ }
+
+ return _gasPrice;
+ }
+
+ public Wei getGasUsed() {
+ if (!BasicUtils.isEmpty(gasUsed)) {
+ _gasUsed = Wei.ofWei(BasicUtils.parseHex(gasUsed));
+ }
+
+ return _gasUsed;
+ }
+
+ public List getTopics() {
+ return topics;
+ }
+
+ public Long getLogIndex() {
+ if (_logIndex == null && !BasicUtils.isEmpty(logIndex)) {
+ _logIndex = BasicUtils.parseHex(logIndex).longValue();
+ }
+ return _logIndex;
+ }
+ //
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (!(o instanceof Log))
+ return false;
+ Log log = (Log) o;
+ return Objects.equals(blockNumber, log.blockNumber) && Objects.equals(address, log.address)
+ && Objects.equals(transactionHash, log.transactionHash) && Objects.equals(transactionIndex, log.transactionIndex)
+ && Objects.equals(logIndex, log.logIndex);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(blockNumber, address, transactionHash, transactionIndex, logIndex);
+ }
+
+ @Override
+ public String toString() {
+ return "Log{" +
+ "blockNumber='" + blockNumber + '\'' +
+ ", address='" + address + '\'' +
+ ", transactionHash='" + transactionHash + '\'' +
+ ", transactionIndex='" + transactionIndex + '\'' +
+ ", timeStamp='" + timeStamp + '\'' +
+ ", data='" + data + '\'' +
+ ", gasPrice='" + gasPrice + '\'' +
+ ", gasUsed='" + gasUsed + '\'' +
+ ", topics=" + topics +
+ ", logIndex='" + logIndex + '\'' +
+ '}';
+ }
+
+ public static LogBuilder builder() {
+ return new LogBuilder();
+ }
+
+ public static final class LogBuilder {
+
+ private Long blockNumber;
+ private String address;
+ private String transactionHash;
+ private Long transactionIndex;
+ private LocalDateTime timeStamp;
+ private String data;
+ private Wei gasPrice;
+ private Wei gasUsed;
+ private List topics;
+ private Long logIndex;
+
+ private LogBuilder() {}
+
+ public LogBuilder withBlockNumber(Long blockNumber) {
+ this.blockNumber = blockNumber;
+ return this;
+ }
+
+ public LogBuilder withAddress(String address) {
+ this.address = address;
+ return this;
+ }
+
+ public LogBuilder withTransactionHash(String transactionHash) {
+ this.transactionHash = transactionHash;
+ return this;
+ }
+
+ public LogBuilder withTransactionIndex(Long transactionIndex) {
+ this.transactionIndex = transactionIndex;
+ return this;
+ }
+
+ public LogBuilder withTimeStamp(LocalDateTime timeStamp) {
+ this.timeStamp = timeStamp;
+ return this;
+ }
+
+ public LogBuilder withData(String data) {
+ this.data = data;
+ return this;
+ }
+
+ public LogBuilder withGasPrice(Wei gasPrice) {
+ this.gasPrice = gasPrice;
+ return this;
+ }
+
+ public LogBuilder withGasUsed(Wei gasUsed) {
+ this.gasUsed = gasUsed;
+ return this;
+ }
+
+ public LogBuilder withTopics(List topics) {
+ this.topics = topics;
+ return this;
+ }
+
+ public LogBuilder withLogIndex(Long logIndex) {
+ this.logIndex = logIndex;
+ return this;
+ }
+
+ public Log build() {
+ Log log = new Log();
+ log.address = this.address;
+ if (this.gasPrice != null) {
+ log._gasPrice = this.gasPrice;
+ }
+ log._logIndex = this.logIndex;
+ log._transactionIndex = this.transactionIndex;
+ log.blockNumber = String.valueOf(this.blockNumber);
+ log.transactionIndex = String.valueOf(this.transactionIndex);
+ if (this.timeStamp != null) {
+ log.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC));
+ log._timeStamp = this.timeStamp;
+ }
+ log.data = this.data;
+ if (this.gasUsed != null) {
+ log._gasUsed = this.gasUsed;
+ }
+ log.logIndex = String.valueOf(this.logIndex);
+ log._blockNumber = this.blockNumber;
+ log.topics = this.topics;
+ log.transactionHash = this.transactionHash;
+ return log;
+ }
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Price.java b/src/main/java/io/goodforgod/api/etherscan/model/Price.java
new file mode 100644
index 0000000..565dbed
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/Price.java
@@ -0,0 +1,123 @@
+package io.goodforgod.api.etherscan.model;
+
+import com.google.gson.annotations.Expose;
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.Objects;
+
+/**
+ * @author GoodforGod
+ * @since 30.10.2018
+ */
+public class Price {
+
+ private BigDecimal ethusd;
+ private BigDecimal ethbtc;
+ private String ethusd_timestamp;
+ private String ethbtc_timestamp;
+ @Expose(deserialize = false, serialize = false)
+ private LocalDateTime _ethusd_timestamp;
+ @Expose(deserialize = false, serialize = false)
+ private LocalDateTime _ethbtc_timestamp;
+
+ protected Price() {}
+
+ public BigDecimal inUsd() {
+ return ethusd;
+ }
+
+ public BigDecimal inBtc() {
+ return ethbtc;
+ }
+
+ public LocalDateTime timestampUsd() {
+ if (_ethusd_timestamp == null && ethusd_timestamp != null) {
+ _ethusd_timestamp = LocalDateTime.ofEpochSecond(Long.parseLong(ethusd_timestamp), 0, ZoneOffset.UTC);
+ }
+ return _ethusd_timestamp;
+ }
+
+ public LocalDateTime timestampBtc() {
+ if (_ethbtc_timestamp == null && ethbtc_timestamp != null) {
+ _ethbtc_timestamp = LocalDateTime.ofEpochSecond(Long.parseLong(ethbtc_timestamp), 0, ZoneOffset.UTC);
+ }
+ return _ethbtc_timestamp;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (!(o instanceof Price))
+ return false;
+ Price price = (Price) o;
+ return Objects.equals(ethusd, price.ethusd) && Objects.equals(ethbtc, price.ethbtc)
+ && Objects.equals(ethusd_timestamp, price.ethusd_timestamp)
+ && Objects.equals(ethbtc_timestamp, price.ethbtc_timestamp);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(ethusd, ethbtc, ethusd_timestamp, ethbtc_timestamp);
+ }
+
+ @Override
+ public String toString() {
+ return "Price{" +
+ "ethusd=" + ethusd +
+ ", ethbtc=" + ethbtc +
+ ", ethusd_timestamp='" + ethusd_timestamp + '\'' +
+ ", ethbtc_timestamp='" + ethbtc_timestamp + '\'' +
+ '}';
+ }
+
+ public static PriceBuilder builder() {
+ return new PriceBuilder();
+ }
+
+ public static final class PriceBuilder {
+
+ private BigDecimal ethusd;
+ private BigDecimal ethbtc;
+ private LocalDateTime ethusdTimestamp;
+ private LocalDateTime ethbtcTimestamp;
+
+ private PriceBuilder() {}
+
+ public PriceBuilder withUsd(BigDecimal ethToUsd) {
+ this.ethusd = ethToUsd;
+ return this;
+ }
+
+ public PriceBuilder withBtc(BigDecimal ethToBtc) {
+ this.ethbtc = ethToBtc;
+ return this;
+ }
+
+ public PriceBuilder withTimestampUsd(LocalDateTime ethToUsdTimestamp) {
+ this.ethusdTimestamp = ethToUsdTimestamp;
+ return this;
+ }
+
+ public PriceBuilder withTimestampBtc(LocalDateTime ethToBtcTimestamp) {
+ this.ethbtcTimestamp = ethToBtcTimestamp;
+ return this;
+ }
+
+ public Price build() {
+ Price price = new Price();
+ price.ethbtc = this.ethbtc;
+ price.ethusd = this.ethusd;
+ if (this.ethbtcTimestamp != null) {
+ price.ethbtc_timestamp = String.valueOf(this.ethbtcTimestamp.toEpochSecond(ZoneOffset.UTC));
+ price._ethbtc_timestamp = this.ethbtcTimestamp;
+ }
+ if (this.ethusdTimestamp != null) {
+ price.ethusd_timestamp = String.valueOf(this.ethusdTimestamp.toEpochSecond(ZoneOffset.UTC));
+ price._ethusd_timestamp = this.ethusdTimestamp;
+ }
+ return price;
+ }
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Status.java b/src/main/java/io/goodforgod/api/etherscan/model/Status.java
new file mode 100644
index 0000000..052c187
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/Status.java
@@ -0,0 +1,80 @@
+package io.goodforgod.api.etherscan.model;
+
+import java.util.Objects;
+
+/**
+ * Contract Execution Status
+ *
+ * @author GoodforGod
+ * @since 30.10.2018
+ */
+public class Status {
+
+ /**
+ * "0" = Pass , isError":"1" = Error during Contract Execution
+ */
+ private int isError;
+ private String errDescription;
+
+ protected Status() {}
+
+ public boolean haveError() {
+ return isError == 1;
+ }
+
+ public String getErrDescription() {
+ return errDescription;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (!(o instanceof Status))
+ return false;
+ Status status = (Status) o;
+ return isError == status.isError && Objects.equals(errDescription, status.errDescription);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(isError, errDescription);
+ }
+
+ @Override
+ public String toString() {
+ return "Status{" +
+ "isError=" + isError +
+ ", errDescription='" + errDescription + '\'' +
+ '}';
+ }
+
+ public static StatusBuilder builder() {
+ return new StatusBuilder();
+ }
+
+ public static final class StatusBuilder {
+
+ private int isError;
+ private String errDescription;
+
+ private StatusBuilder() {}
+
+ public StatusBuilder withIsError(int isError) {
+ this.isError = isError;
+ return this;
+ }
+
+ public StatusBuilder withErrDescription(String errDescription) {
+ this.errDescription = errDescription;
+ return this;
+ }
+
+ public Status build() {
+ Status status = new Status();
+ status.isError = this.isError;
+ status.errDescription = this.errDescription;
+ return status;
+ }
+ }
+}
diff --git a/src/main/java/io/api/etherscan/model/TokenBalance.java b/src/main/java/io/goodforgod/api/etherscan/model/TokenBalance.java
similarity index 69%
rename from src/main/java/io/api/etherscan/model/TokenBalance.java
rename to src/main/java/io/goodforgod/api/etherscan/model/TokenBalance.java
index d057992..bb40ee2 100644
--- a/src/main/java/io/api/etherscan/model/TokenBalance.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/TokenBalance.java
@@ -1,11 +1,8 @@
-package io.api.etherscan.model;
+package io.goodforgod.api.etherscan.model;
-import java.math.BigInteger;
import java.util.Objects;
/**
- * ! NO DESCRIPTION !
- *
* @author GoodforGod
* @since 31.10.2018
*/
@@ -13,7 +10,7 @@ public class TokenBalance extends Balance {
private final String tokenContract;
- public TokenBalance(String address, BigInteger balance, String tokenContract) {
+ public TokenBalance(String address, Wei balance, String tokenContract) {
super(address, balance);
this.tokenContract = tokenContract;
}
@@ -26,20 +23,17 @@ public String getContract() {
public boolean equals(Object o) {
if (this == o)
return true;
- if (o == null || getClass() != o.getClass())
+ if (!(o instanceof TokenBalance))
return false;
if (!super.equals(o))
return false;
-
TokenBalance that = (TokenBalance) o;
return Objects.equals(tokenContract, that.tokenContract);
}
@Override
public int hashCode() {
- int result = super.hashCode();
- result = 31 * result + (tokenContract != null ? tokenContract.hashCode() : 0);
- return result;
+ return Objects.hash(super.hashCode(), tokenContract);
}
@Override
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Tx.java b/src/main/java/io/goodforgod/api/etherscan/model/Tx.java
new file mode 100644
index 0000000..7ef0e22
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/Tx.java
@@ -0,0 +1,209 @@
+package io.goodforgod.api.etherscan.model;
+
+import io.goodforgod.api.etherscan.util.BasicUtils;
+import java.math.BigInteger;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+
+/**
+ * @author GoodforGod
+ * @since 28.10.2018
+ */
+public class Tx extends BlockTx {
+
+ private BigInteger value;
+ private String isError;
+ private String txreceipt_status;
+
+ protected Tx() {}
+
+ //
+ public BigInteger getValue() {
+ return value;
+ }
+
+ public boolean haveError() {
+ return !BasicUtils.isEmpty(isError) && !isError.equals("0");
+ }
+
+ public String getTxReceiptStatus() {
+ return txreceipt_status;
+ }
+ //
+
+ @Override
+ public String toString() {
+ return "Tx{" +
+ "value=" + value +
+ ", isError='" + isError + '\'' +
+ ", txreceipt_status='" + txreceipt_status + '\'' +
+ ", nonce=" + nonce +
+ ", blockHash='" + blockHash + '\'' +
+ ", transactionIndex=" + transactionIndex +
+ ", confirmations=" + confirmations +
+ ", gasPrice=" + gasPrice +
+ ", cumulativeGasUsed=" + cumulativeGasUsed +
+ ", blockNumber=" + blockNumber +
+ ", timeStamp='" + timeStamp + '\'' +
+ ", hash='" + hash + '\'' +
+ ", from='" + from + '\'' +
+ ", to='" + to + '\'' +
+ ", contractAddress='" + contractAddress + '\'' +
+ ", input='" + input + '\'' +
+ ", gas=" + gas +
+ ", gasUsed=" + gasUsed +
+ '}';
+ }
+
+ public static TxBuilder builder() {
+ return new TxBuilder();
+ }
+
+ public static final class TxBuilder {
+
+ private long blockNumber;
+ private LocalDateTime timeStamp;
+ private String hash;
+ private String from;
+ private String to;
+ private BigInteger value;
+ private String contractAddress;
+ private String input;
+ private Wei gas;
+ private Wei gasUsed;
+ private long nonce;
+ private String blockHash;
+ private int transactionIndex;
+ private Wei gasPrice;
+ private Wei cumulativeGasUsed;
+ private long confirmations;
+ private String isError;
+ private String txReceiptStatus;
+
+ private TxBuilder() {}
+
+ public TxBuilder withBlockNumber(long blockNumber) {
+ this.blockNumber = blockNumber;
+ return this;
+ }
+
+ public TxBuilder withTimeStamp(LocalDateTime timeStamp) {
+ this.timeStamp = timeStamp;
+ return this;
+ }
+
+ public TxBuilder withHash(String hash) {
+ this.hash = hash;
+ return this;
+ }
+
+ public TxBuilder withFrom(String from) {
+ this.from = from;
+ return this;
+ }
+
+ public TxBuilder withTo(String to) {
+ this.to = to;
+ return this;
+ }
+
+ public TxBuilder withValue(BigInteger value) {
+ this.value = value;
+ return this;
+ }
+
+ public TxBuilder withContractAddress(String contractAddress) {
+ this.contractAddress = contractAddress;
+ return this;
+ }
+
+ public TxBuilder withInput(String input) {
+ this.input = input;
+ return this;
+ }
+
+ public TxBuilder withGas(Wei gas) {
+ this.gas = gas;
+ return this;
+ }
+
+ public TxBuilder withGasUsed(Wei gasUsed) {
+ this.gasUsed = gasUsed;
+ return this;
+ }
+
+ public TxBuilder withNonce(long nonce) {
+ this.nonce = nonce;
+ return this;
+ }
+
+ public TxBuilder withBlockHash(String blockHash) {
+ this.blockHash = blockHash;
+ return this;
+ }
+
+ public TxBuilder withTransactionIndex(int transactionIndex) {
+ this.transactionIndex = transactionIndex;
+ return this;
+ }
+
+ public TxBuilder withGasPrice(Wei gasPrice) {
+ this.gasPrice = gasPrice;
+ return this;
+ }
+
+ public TxBuilder withCumulativeGasUsed(Wei cumulativeGasUsed) {
+ this.cumulativeGasUsed = cumulativeGasUsed;
+ return this;
+ }
+
+ public TxBuilder withConfirmations(long confirmations) {
+ this.confirmations = confirmations;
+ return this;
+ }
+
+ public TxBuilder withIsError(String isError) {
+ this.isError = isError;
+ return this;
+ }
+
+ public TxBuilder withTxReceiptStatus(String txReceiptStatus) {
+ this.txReceiptStatus = txReceiptStatus;
+ return this;
+ }
+
+ public Tx build() {
+ Tx tx = new Tx();
+ tx.isError = this.isError;
+ tx.blockHash = this.blockHash;
+ tx.hash = this.hash;
+ if (this.gas != null) {
+ tx.gas = this.gas.asWei();
+ }
+ if (this.gasUsed != null) {
+ tx.gasUsed = this.gasUsed.asWei();
+ }
+ if (this.gasPrice != null) {
+ tx.gasPrice = this.gasPrice.asWei();
+ }
+ if (this.cumulativeGasUsed != null) {
+ tx.cumulativeGasUsed = this.cumulativeGasUsed.asWei();
+ }
+ tx.from = this.from;
+ tx.txreceipt_status = this.txReceiptStatus;
+ tx.contractAddress = this.contractAddress;
+ tx.value = this.value;
+ tx.transactionIndex = this.transactionIndex;
+ tx.confirmations = this.confirmations;
+ if (this.timeStamp != null) {
+ tx.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC));
+ tx._timeStamp = this.timeStamp;
+ }
+ tx.nonce = this.nonce;
+ tx.blockNumber = this.blockNumber;
+ tx.to = this.to;
+ tx.input = this.input;
+ return tx;
+ }
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java b/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java
new file mode 100644
index 0000000..16d4457
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java
@@ -0,0 +1,239 @@
+package io.goodforgod.api.etherscan.model;
+
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.Objects;
+
+/**
+ * @author GoodforGod
+ * @since 28.10.2018
+ */
+public class TxErc1155 extends BlockTx {
+
+ private String tokenID;
+ private String tokenName;
+ private String tokenSymbol;
+ private String tokenValue;
+
+ protected TxErc1155() {}
+
+ //
+ public String getTokenID() {
+ return tokenID;
+ }
+
+ public String getTokenName() {
+ return tokenName;
+ }
+
+ public String getTokenSymbol() {
+ return tokenSymbol;
+ }
+
+ public String getTokenValue() {
+ return tokenValue;
+ }
+ //
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (!(o instanceof TxErc1155))
+ return false;
+ if (!super.equals(o))
+ return false;
+ TxErc1155 txErc1155 = (TxErc1155) o;
+ return Objects.equals(tokenID, txErc1155.tokenID) && Objects.equals(tokenName, txErc1155.tokenName)
+ && Objects.equals(tokenSymbol, txErc1155.tokenSymbol) && Objects.equals(tokenValue, txErc1155.tokenValue);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), tokenID, tokenName, tokenSymbol, tokenValue);
+ }
+
+ @Override
+ public String toString() {
+ return "TxErc1155{" +
+ "tokenID='" + tokenID + '\'' +
+ ", tokenName='" + tokenName + '\'' +
+ ", tokenSymbol='" + tokenSymbol + '\'' +
+ ", tokenValue='" + tokenValue + '\'' +
+ ", nonce=" + nonce +
+ ", blockHash='" + blockHash + '\'' +
+ ", transactionIndex=" + transactionIndex +
+ ", confirmations=" + confirmations +
+ ", gasPrice=" + gasPrice +
+ ", cumulativeGasUsed=" + cumulativeGasUsed +
+ ", blockNumber=" + blockNumber +
+ ", timeStamp='" + timeStamp + '\'' +
+ ", hash='" + hash + '\'' +
+ ", from='" + from + '\'' +
+ ", to='" + to + '\'' +
+ ", contractAddress='" + contractAddress + '\'' +
+ ", input='" + input + '\'' +
+ ", gas=" + gas +
+ ", gasUsed=" + gasUsed +
+ '}';
+ }
+
+ public static TxErc1155Builder builder() {
+ return new TxErc1155Builder();
+ }
+
+ public static final class TxErc1155Builder {
+
+ private long blockNumber;
+ private LocalDateTime timeStamp;
+ private String hash;
+ private String from;
+ private String to;
+ private String contractAddress;
+ private String input;
+ private long nonce;
+ private String blockHash;
+ private String tokenID;
+ private String tokenName;
+ private String tokenSymbol;
+ private String tokenValue;
+ private int transactionIndex;
+ private Wei gas;
+ private Wei gasUsed;
+ private Wei gasPrice;
+ private Wei cumulativeGasUsed;
+ private long confirmations;
+
+ private TxErc1155Builder() {}
+
+ public TxErc1155Builder withBlockNumber(long blockNumber) {
+ this.blockNumber = blockNumber;
+ return this;
+ }
+
+ public TxErc1155Builder withTimeStamp(LocalDateTime timeStamp) {
+ this.timeStamp = timeStamp;
+ return this;
+ }
+
+ public TxErc1155Builder withHash(String hash) {
+ this.hash = hash;
+ return this;
+ }
+
+ public TxErc1155Builder withFrom(String from) {
+ this.from = from;
+ return this;
+ }
+
+ public TxErc1155Builder withTo(String to) {
+ this.to = to;
+ return this;
+ }
+
+ public TxErc1155Builder withContractAddress(String contractAddress) {
+ this.contractAddress = contractAddress;
+ return this;
+ }
+
+ public TxErc1155Builder withInput(String input) {
+ this.input = input;
+ return this;
+ }
+
+ public TxErc1155Builder withGas(Wei gas) {
+ this.gas = gas;
+ return this;
+ }
+
+ public TxErc1155Builder withGasUsed(Wei gasUsed) {
+ this.gasUsed = gasUsed;
+ return this;
+ }
+
+ public TxErc1155Builder withNonce(long nonce) {
+ this.nonce = nonce;
+ return this;
+ }
+
+ public TxErc1155Builder withBlockHash(String blockHash) {
+ this.blockHash = blockHash;
+ return this;
+ }
+
+ public TxErc1155Builder withTokenID(String tokenID) {
+ this.tokenID = tokenID;
+ return this;
+ }
+
+ public TxErc1155Builder withTokenName(String tokenName) {
+ this.tokenName = tokenName;
+ return this;
+ }
+
+ public TxErc1155Builder withTokenSymbol(String tokenSymbol) {
+ this.tokenSymbol = tokenSymbol;
+ return this;
+ }
+
+ public TxErc1155Builder withTokenDecimal(String tokenDecimal) {
+ this.tokenValue = tokenDecimal;
+ return this;
+ }
+
+ public TxErc1155Builder withTransactionIndex(int transactionIndex) {
+ this.transactionIndex = transactionIndex;
+ return this;
+ }
+
+ public TxErc1155Builder withGasPrice(Wei gasPrice) {
+ this.gasPrice = gasPrice;
+ return this;
+ }
+
+ public TxErc1155Builder withCumulativeGasUsed(Wei cumulativeGasUsed) {
+ this.cumulativeGasUsed = cumulativeGasUsed;
+ return this;
+ }
+
+ public TxErc1155Builder withConfirmations(long confirmations) {
+ this.confirmations = confirmations;
+ return this;
+ }
+
+ public TxErc1155 build() {
+ TxErc1155 txERC1155 = new TxErc1155();
+ txERC1155.tokenName = this.tokenName;
+ txERC1155.hash = this.hash;
+ txERC1155.nonce = this.nonce;
+ txERC1155.from = this.from;
+ if (this.gas != null) {
+ txERC1155.gas = this.gas.asWei();
+ }
+ if (this.gasUsed != null) {
+ txERC1155.gasUsed = this.gasUsed.asWei();
+ }
+ if (this.gasPrice != null) {
+ txERC1155.gasPrice = this.gasPrice.asWei();
+ }
+ if (this.cumulativeGasUsed != null) {
+ txERC1155.cumulativeGasUsed = this.cumulativeGasUsed.asWei();
+ }
+ txERC1155.contractAddress = this.contractAddress;
+ txERC1155.tokenID = this.tokenID;
+ if (this.timeStamp != null) {
+ txERC1155.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC));
+ txERC1155._timeStamp = this.timeStamp;
+ }
+ txERC1155.blockNumber = this.blockNumber;
+ txERC1155.tokenValue = this.tokenValue;
+ txERC1155.transactionIndex = this.transactionIndex;
+ txERC1155.to = this.to;
+ txERC1155.confirmations = this.confirmations;
+ txERC1155.input = this.input;
+ txERC1155.blockHash = this.blockHash;
+ txERC1155.tokenSymbol = this.tokenSymbol;
+ return txERC1155;
+ }
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java b/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java
new file mode 100644
index 0000000..3dc22fd
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java
@@ -0,0 +1,240 @@
+package io.goodforgod.api.etherscan.model;
+
+import java.math.BigInteger;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.Objects;
+
+/**
+ * @author GoodforGod
+ * @since 28.10.2018
+ */
+public class TxErc20 extends BlockTx {
+
+ private BigInteger value;
+ private String tokenName;
+ private String tokenSymbol;
+ private String tokenDecimal;
+
+ protected TxErc20() {}
+
+ //
+ public BigInteger getValue() {
+ return value;
+ }
+
+ public String getTokenName() {
+ return tokenName;
+ }
+
+ public String getTokenSymbol() {
+ return tokenSymbol;
+ }
+
+ public String getTokenDecimal() {
+ return tokenDecimal;
+ }
+ //
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (!(o instanceof TxErc20))
+ return false;
+ if (!super.equals(o))
+ return false;
+ TxErc20 txErc20 = (TxErc20) o;
+ return Objects.equals(tokenName, txErc20.tokenName) && Objects.equals(tokenSymbol, txErc20.tokenSymbol)
+ && Objects.equals(tokenDecimal, txErc20.tokenDecimal);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), tokenName, tokenSymbol, tokenDecimal);
+ }
+
+ @Override
+ public String toString() {
+ return "TxErc20{" +
+ "value=" + value +
+ ", tokenName='" + tokenName + '\'' +
+ ", tokenSymbol='" + tokenSymbol + '\'' +
+ ", tokenDecimal='" + tokenDecimal + '\'' +
+ ", nonce=" + nonce +
+ ", blockHash='" + blockHash + '\'' +
+ ", transactionIndex=" + transactionIndex +
+ ", confirmations=" + confirmations +
+ ", gasPrice=" + gasPrice +
+ ", cumulativeGasUsed=" + cumulativeGasUsed +
+ ", blockNumber=" + blockNumber +
+ ", timeStamp='" + timeStamp + '\'' +
+ ", hash='" + hash + '\'' +
+ ", from='" + from + '\'' +
+ ", to='" + to + '\'' +
+ ", contractAddress='" + contractAddress + '\'' +
+ ", input='" + input + '\'' +
+ ", gas=" + gas +
+ ", gasUsed=" + gasUsed +
+ '}';
+ }
+
+ public static TxERC20Builder builder() {
+ return new TxERC20Builder();
+ }
+
+ public static final class TxERC20Builder {
+
+ private long blockNumber;
+ private LocalDateTime timeStamp;
+ private String hash;
+ private String from;
+ private String to;
+ private BigInteger value;
+ private String contractAddress;
+ private String input;
+ private Wei gas;
+ private Wei gasUsed;
+ private long nonce;
+ private String blockHash;
+ private String tokenName;
+ private String tokenSymbol;
+ private String tokenDecimal;
+ private int transactionIndex;
+ private Wei gasPrice;
+ private Wei cumulativeGasUsed;
+ private long confirmations;
+
+ private TxERC20Builder() {}
+
+ public TxERC20Builder withBlockNumber(long blockNumber) {
+ this.blockNumber = blockNumber;
+ return this;
+ }
+
+ public TxERC20Builder withTimeStamp(LocalDateTime timeStamp) {
+ this.timeStamp = timeStamp;
+ return this;
+ }
+
+ public TxERC20Builder withHash(String hash) {
+ this.hash = hash;
+ return this;
+ }
+
+ public TxERC20Builder withFrom(String from) {
+ this.from = from;
+ return this;
+ }
+
+ public TxERC20Builder withTo(String to) {
+ this.to = to;
+ return this;
+ }
+
+ public TxERC20Builder withValue(BigInteger value) {
+ this.value = value;
+ return this;
+ }
+
+ public TxERC20Builder withContractAddress(String contractAddress) {
+ this.contractAddress = contractAddress;
+ return this;
+ }
+
+ public TxERC20Builder withInput(String input) {
+ this.input = input;
+ return this;
+ }
+
+ public TxERC20Builder withGas(Wei gas) {
+ this.gas = gas;
+ return this;
+ }
+
+ public TxERC20Builder withGasUsed(Wei gasUsed) {
+ this.gasUsed = gasUsed;
+ return this;
+ }
+
+ public TxERC20Builder withNonce(long nonce) {
+ this.nonce = nonce;
+ return this;
+ }
+
+ public TxERC20Builder withBlockHash(String blockHash) {
+ this.blockHash = blockHash;
+ return this;
+ }
+
+ public TxERC20Builder withTokenName(String tokenName) {
+ this.tokenName = tokenName;
+ return this;
+ }
+
+ public TxERC20Builder withTokenSymbol(String tokenSymbol) {
+ this.tokenSymbol = tokenSymbol;
+ return this;
+ }
+
+ public TxERC20Builder withTokenDecimal(String tokenDecimal) {
+ this.tokenDecimal = tokenDecimal;
+ return this;
+ }
+
+ public TxERC20Builder withTransactionIndex(int transactionIndex) {
+ this.transactionIndex = transactionIndex;
+ return this;
+ }
+
+ public TxERC20Builder withGasPrice(Wei gasPrice) {
+ this.gasPrice = gasPrice;
+ return this;
+ }
+
+ public TxERC20Builder withCumulativeGasUsed(Wei cumulativeGasUsed) {
+ this.cumulativeGasUsed = cumulativeGasUsed;
+ return this;
+ }
+
+ public TxERC20Builder withConfirmations(long confirmations) {
+ this.confirmations = confirmations;
+ return this;
+ }
+
+ public TxErc20 build() {
+ TxErc20 txERC20 = new TxErc20();
+ txERC20.tokenName = this.tokenName;
+ txERC20.hash = this.hash;
+ if (this.gas != null) {
+ txERC20.gas = this.gas.asWei();
+ }
+ if (this.gasUsed != null) {
+ txERC20.gasUsed = this.gasUsed.asWei();
+ }
+ if (this.gasPrice != null) {
+ txERC20.gasPrice = this.gasPrice.asWei();
+ }
+ if (this.cumulativeGasUsed != null) {
+ txERC20.cumulativeGasUsed = this.cumulativeGasUsed.asWei();
+ }
+ txERC20.from = this.from;
+ txERC20.tokenSymbol = this.tokenSymbol;
+ txERC20.transactionIndex = this.transactionIndex;
+ txERC20.contractAddress = this.contractAddress;
+ txERC20.nonce = this.nonce;
+ txERC20.confirmations = this.confirmations;
+ txERC20.value = this.value;
+ if (this.timeStamp != null) {
+ txERC20.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC));
+ txERC20._timeStamp = this.timeStamp;
+ }
+ txERC20.blockHash = this.blockHash;
+ txERC20.blockNumber = this.blockNumber;
+ txERC20.to = this.to;
+ txERC20.input = this.input;
+ txERC20.tokenDecimal = this.tokenDecimal;
+ return txERC20;
+ }
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java b/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java
new file mode 100644
index 0000000..2180019
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java
@@ -0,0 +1,239 @@
+package io.goodforgod.api.etherscan.model;
+
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.Objects;
+
+/**
+ * @author GoodforGod
+ * @since 28.10.2018
+ */
+public class TxErc721 extends BlockTx {
+
+ private String tokenID;
+ private String tokenName;
+ private String tokenSymbol;
+ private String tokenDecimal;
+
+ protected TxErc721() {}
+
+ //
+ public String getTokenID() {
+ return tokenID;
+ }
+
+ public String getTokenName() {
+ return tokenName;
+ }
+
+ public String getTokenSymbol() {
+ return tokenSymbol;
+ }
+
+ public String getTokenDecimal() {
+ return tokenDecimal;
+ }
+ //
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (!(o instanceof TxErc721))
+ return false;
+ if (!super.equals(o))
+ return false;
+ TxErc721 txErc721 = (TxErc721) o;
+ return Objects.equals(tokenID, txErc721.tokenID) && Objects.equals(tokenName, txErc721.tokenName)
+ && Objects.equals(tokenSymbol, txErc721.tokenSymbol) && Objects.equals(tokenDecimal, txErc721.tokenDecimal);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), tokenID, tokenName, tokenSymbol, tokenDecimal);
+ }
+
+ @Override
+ public String toString() {
+ return "TxErc721{" +
+ "tokenID='" + tokenID + '\'' +
+ ", tokenName='" + tokenName + '\'' +
+ ", tokenSymbol='" + tokenSymbol + '\'' +
+ ", tokenDecimal='" + tokenDecimal + '\'' +
+ ", nonce=" + nonce +
+ ", blockHash='" + blockHash + '\'' +
+ ", transactionIndex=" + transactionIndex +
+ ", confirmations=" + confirmations +
+ ", gasPrice=" + gasPrice +
+ ", cumulativeGasUsed=" + cumulativeGasUsed +
+ ", blockNumber=" + blockNumber +
+ ", timeStamp='" + timeStamp + '\'' +
+ ", hash='" + hash + '\'' +
+ ", from='" + from + '\'' +
+ ", to='" + to + '\'' +
+ ", contractAddress='" + contractAddress + '\'' +
+ ", input='" + input + '\'' +
+ ", gas=" + gas +
+ ", gasUsed=" + gasUsed +
+ '}';
+ }
+
+ public static TxERC721Builder builder() {
+ return new TxERC721Builder();
+ }
+
+ public static final class TxERC721Builder {
+
+ private long blockNumber;
+ private LocalDateTime timeStamp;
+ private String hash;
+ private String from;
+ private String to;
+ private String contractAddress;
+ private String input;
+ private long nonce;
+ private String blockHash;
+ private String tokenID;
+ private String tokenName;
+ private String tokenSymbol;
+ private String tokenDecimal;
+ private int transactionIndex;
+ private Wei gas;
+ private Wei gasUsed;
+ private Wei gasPrice;
+ private Wei cumulativeGasUsed;
+ private long confirmations;
+
+ private TxERC721Builder() {}
+
+ public TxERC721Builder withBlockNumber(long blockNumber) {
+ this.blockNumber = blockNumber;
+ return this;
+ }
+
+ public TxERC721Builder withTimeStamp(LocalDateTime timeStamp) {
+ this.timeStamp = timeStamp;
+ return this;
+ }
+
+ public TxERC721Builder withHash(String hash) {
+ this.hash = hash;
+ return this;
+ }
+
+ public TxERC721Builder withFrom(String from) {
+ this.from = from;
+ return this;
+ }
+
+ public TxERC721Builder withTo(String to) {
+ this.to = to;
+ return this;
+ }
+
+ public TxERC721Builder withContractAddress(String contractAddress) {
+ this.contractAddress = contractAddress;
+ return this;
+ }
+
+ public TxERC721Builder withInput(String input) {
+ this.input = input;
+ return this;
+ }
+
+ public TxERC721Builder withGas(Wei gas) {
+ this.gas = gas;
+ return this;
+ }
+
+ public TxERC721Builder withGasUsed(Wei gasUsed) {
+ this.gasUsed = gasUsed;
+ return this;
+ }
+
+ public TxERC721Builder withNonce(long nonce) {
+ this.nonce = nonce;
+ return this;
+ }
+
+ public TxERC721Builder withBlockHash(String blockHash) {
+ this.blockHash = blockHash;
+ return this;
+ }
+
+ public TxERC721Builder withTokenID(String tokenID) {
+ this.tokenID = tokenID;
+ return this;
+ }
+
+ public TxERC721Builder withTokenName(String tokenName) {
+ this.tokenName = tokenName;
+ return this;
+ }
+
+ public TxERC721Builder withTokenSymbol(String tokenSymbol) {
+ this.tokenSymbol = tokenSymbol;
+ return this;
+ }
+
+ public TxERC721Builder withTokenDecimal(String tokenDecimal) {
+ this.tokenDecimal = tokenDecimal;
+ return this;
+ }
+
+ public TxERC721Builder withTransactionIndex(int transactionIndex) {
+ this.transactionIndex = transactionIndex;
+ return this;
+ }
+
+ public TxERC721Builder withGasPrice(Wei gasPrice) {
+ this.gasPrice = gasPrice;
+ return this;
+ }
+
+ public TxERC721Builder withCumulativeGasUsed(Wei cumulativeGasUsed) {
+ this.cumulativeGasUsed = cumulativeGasUsed;
+ return this;
+ }
+
+ public TxERC721Builder withConfirmations(long confirmations) {
+ this.confirmations = confirmations;
+ return this;
+ }
+
+ public TxErc721 build() {
+ TxErc721 txERC721 = new TxErc721();
+ txERC721.tokenName = this.tokenName;
+ txERC721.hash = this.hash;
+ txERC721.nonce = this.nonce;
+ txERC721.from = this.from;
+ if (this.gas != null) {
+ txERC721.gas = this.gas.asWei();
+ }
+ if (this.gasUsed != null) {
+ txERC721.gasUsed = this.gasUsed.asWei();
+ }
+ if (this.gasPrice != null) {
+ txERC721.gasPrice = this.gasPrice.asWei();
+ }
+ if (this.cumulativeGasUsed != null) {
+ txERC721.cumulativeGasUsed = this.cumulativeGasUsed.asWei();
+ }
+ txERC721.contractAddress = this.contractAddress;
+ txERC721.tokenID = this.tokenID;
+ if (this.timeStamp != null) {
+ txERC721.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC));
+ txERC721._timeStamp = this.timeStamp;
+ }
+ txERC721.blockNumber = this.blockNumber;
+ txERC721.tokenDecimal = this.tokenDecimal;
+ txERC721.transactionIndex = this.transactionIndex;
+ txERC721.to = this.to;
+ txERC721.confirmations = this.confirmations;
+ txERC721.input = this.input;
+ txERC721.blockHash = this.blockHash;
+ txERC721.tokenSymbol = this.tokenSymbol;
+ return txERC721;
+ }
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java b/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java
new file mode 100644
index 0000000..a61cf83
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java
@@ -0,0 +1,207 @@
+package io.goodforgod.api.etherscan.model;
+
+import java.math.BigInteger;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.Objects;
+
+/**
+ * @author GoodforGod
+ * @since 29.10.2018
+ */
+public class TxInternal extends BaseTx {
+
+ private BigInteger value;
+ private String type;
+ private String traceId;
+ private int isError;
+ private String errCode;
+
+ protected TxInternal() {}
+
+ //
+ public BigInteger getValue() {
+ return value;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public long getTraceId() {
+ return (traceId == null)
+ ? 0
+ : Long.parseLong(traceId);
+ }
+
+ public String getTraceIdAsString() {
+ return traceId;
+ }
+
+ public boolean haveError() {
+ return isError == 1;
+ }
+
+ public String getErrCode() {
+ return errCode;
+ }
+ //
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (!(o instanceof TxInternal))
+ return false;
+ if (!super.equals(o))
+ return false;
+ TxInternal that = (TxInternal) o;
+ return isError == that.isError && Objects.equals(value, that.value) && Objects.equals(type, that.type)
+ && Objects.equals(traceId, that.traceId) && Objects.equals(errCode, that.errCode);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), value, type, traceId, isError, errCode);
+ }
+
+ @Override
+ public String toString() {
+ return "TxInternal{" +
+ "value=" + value +
+ ", type='" + type + '\'' +
+ ", traceId='" + traceId + '\'' +
+ ", isError=" + isError +
+ ", errCode='" + errCode + '\'' +
+ ", blockNumber=" + blockNumber +
+ ", timeStamp='" + timeStamp + '\'' +
+ ", hash='" + hash + '\'' +
+ ", from='" + from + '\'' +
+ ", to='" + to + '\'' +
+ ", contractAddress='" + contractAddress + '\'' +
+ ", input='" + input + '\'' +
+ ", gas=" + gas +
+ ", gasUsed=" + gasUsed +
+ '}';
+ }
+
+ public static TxInternalBuilder builder() {
+ return new TxInternalBuilder();
+ }
+
+ public static final class TxInternalBuilder {
+
+ private long blockNumber;
+ private LocalDateTime timeStamp;
+ private String hash;
+ private String from;
+ private String to;
+ private BigInteger value;
+ private String contractAddress;
+ private String input;
+ private Wei gas;
+ private Wei gasUsed;
+ private String type;
+ private String traceId;
+ private int isError;
+ private String errCode;
+
+ private TxInternalBuilder() {}
+
+ public TxInternalBuilder withBlockNumber(long blockNumber) {
+ this.blockNumber = blockNumber;
+ return this;
+ }
+
+ public TxInternalBuilder withTimeStamp(LocalDateTime timeStamp) {
+ this.timeStamp = timeStamp;
+ return this;
+ }
+
+ public TxInternalBuilder withHash(String hash) {
+ this.hash = hash;
+ return this;
+ }
+
+ public TxInternalBuilder withFrom(String from) {
+ this.from = from;
+ return this;
+ }
+
+ public TxInternalBuilder withTo(String to) {
+ this.to = to;
+ return this;
+ }
+
+ public TxInternalBuilder withValue(BigInteger value) {
+ this.value = value;
+ return this;
+ }
+
+ public TxInternalBuilder withContractAddress(String contractAddress) {
+ this.contractAddress = contractAddress;
+ return this;
+ }
+
+ public TxInternalBuilder withInput(String input) {
+ this.input = input;
+ return this;
+ }
+
+ public TxInternalBuilder withGas(Wei gas) {
+ this.gas = gas;
+ return this;
+ }
+
+ public TxInternalBuilder withGasUsed(Wei gasUsed) {
+ this.gasUsed = gasUsed;
+ return this;
+ }
+
+ public TxInternalBuilder withType(String type) {
+ this.type = type;
+ return this;
+ }
+
+ public TxInternalBuilder withTraceId(String traceId) {
+ this.traceId = traceId;
+ return this;
+ }
+
+ public TxInternalBuilder withIsError(int isError) {
+ this.isError = isError;
+ return this;
+ }
+
+ public TxInternalBuilder withErrCode(String errCode) {
+ this.errCode = errCode;
+ return this;
+ }
+
+ public TxInternal build() {
+ TxInternal txInternal = new TxInternal();
+ txInternal.hash = this.hash;
+ if (this.gas != null) {
+ txInternal.gas = this.gas.asWei();
+ }
+ if (this.gasUsed != null) {
+ txInternal.gasUsed = this.gasUsed.asWei();
+ }
+ txInternal.traceId = this.traceId;
+ txInternal.type = this.type;
+ txInternal.from = this.from;
+ txInternal.contractAddress = this.contractAddress;
+ txInternal.value = this.value;
+ if (this.timeStamp != null) {
+ txInternal.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC));
+ txInternal._timeStamp = this.timeStamp;
+ }
+ txInternal.errCode = this.errCode;
+ txInternal.blockNumber = this.blockNumber;
+ txInternal.isError = this.isError;
+ txInternal.to = this.to;
+ txInternal.input = this.input;
+ return txInternal;
+ }
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Wei.java b/src/main/java/io/goodforgod/api/etherscan/model/Wei.java
new file mode 100644
index 0000000..3180478
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/Wei.java
@@ -0,0 +1,146 @@
+package io.goodforgod.api.etherscan.model;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.math.RoundingMode;
+import java.util.Objects;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author GoodforGod
+ * @since 30.10.2018
+ */
+public class Wei implements Comparable {
+
+ private static final BigDecimal KWEI_POW = BigDecimal.ONE.pow(3);
+ private static final BigDecimal MWEI_POW = BigDecimal.ONE.pow(6);
+ private static final BigDecimal GWEI_POW = BigDecimal.ONE.pow(9);
+ private static final BigDecimal WEI_POW = BigDecimal.ONE.pow(18);
+
+ private final BigInteger result;
+
+ private Wei(BigInteger value) {
+ this.result = value;
+ }
+
+ public static Wei ofWei(int value) {
+ return ofWei(BigInteger.valueOf(value));
+ }
+
+ public static Wei ofWei(long value) {
+ return ofWei(BigInteger.valueOf(value));
+ }
+
+ public static Wei ofWei(BigInteger value) {
+ return new Wei(value);
+ }
+
+ public static Wei ofKwei(int value) {
+ return ofKwei(BigInteger.valueOf(value));
+ }
+
+ public static Wei ofKwei(long value) {
+ return ofKwei(BigInteger.valueOf(value));
+ }
+
+ public static Wei ofKwei(BigDecimal value) {
+ return new Wei(value.multiply(KWEI_POW).toBigInteger());
+ }
+
+ public static Wei ofKwei(BigInteger value) {
+ return new Wei(value.multiply(KWEI_POW.toBigInteger()));
+ }
+
+ public static Wei ofMwei(int value) {
+ return ofMwei(BigInteger.valueOf(value));
+ }
+
+ public static Wei ofMwei(long value) {
+ return ofMwei(BigInteger.valueOf(value));
+ }
+
+ public static Wei ofMwei(BigDecimal value) {
+ return new Wei(value.multiply(MWEI_POW).toBigInteger());
+ }
+
+ public static Wei ofMwei(BigInteger value) {
+ return new Wei(value.multiply(MWEI_POW.toBigInteger()));
+ }
+
+ public static Wei ofGwei(int value) {
+ return ofGwei(BigInteger.valueOf(value));
+ }
+
+ public static Wei ofGwei(long value) {
+ return ofGwei(BigInteger.valueOf(value));
+ }
+
+ public static Wei ofGwei(BigDecimal value) {
+ return new Wei(value.multiply(GWEI_POW).toBigInteger());
+ }
+
+ public static Wei ofGwei(BigInteger value) {
+ return new Wei(value.multiply(GWEI_POW.toBigInteger()));
+ }
+
+ public static Wei ofEther(int value) {
+ return ofEther(BigInteger.valueOf(value));
+ }
+
+ public static Wei ofEther(long value) {
+ return ofEther(BigInteger.valueOf(value));
+ }
+
+ public static Wei ofEther(BigDecimal value) {
+ return new Wei(value.multiply(WEI_POW).toBigInteger());
+ }
+
+ public static Wei ofEther(BigInteger value) {
+ return new Wei(value.multiply(WEI_POW.toBigInteger()));
+ }
+
+ public BigInteger asWei() {
+ return result;
+ }
+
+ public BigDecimal asKwei() {
+ return new BigDecimal(result).divide(KWEI_POW, RoundingMode.HALF_UP);
+ }
+
+ public BigDecimal asMwei() {
+ return new BigDecimal(result).divide(MWEI_POW, RoundingMode.HALF_UP);
+ }
+
+ public BigDecimal asGwei() {
+ return new BigDecimal(result).divide(GWEI_POW, RoundingMode.HALF_UP);
+ }
+
+ public BigDecimal asEther() {
+ return new BigDecimal(result).divide(WEI_POW, RoundingMode.HALF_UP);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (!(o instanceof Wei))
+ return false;
+ Wei wei = (Wei) o;
+ return Objects.equals(result, wei.result);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(result);
+ }
+
+ @Override
+ public int compareTo(@NotNull Wei o) {
+ return result.compareTo(o.result);
+ }
+
+ @Override
+ public String toString() {
+ return result.toString();
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java
new file mode 100644
index 0000000..4a2b624
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java
@@ -0,0 +1,356 @@
+package io.goodforgod.api.etherscan.model.proxy;
+
+import com.google.gson.annotations.Expose;
+import io.goodforgod.api.etherscan.model.Wei;
+import io.goodforgod.api.etherscan.util.BasicUtils;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.List;
+import java.util.Objects;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author GoodforGod
+ * @since 31.10.2018
+ */
+public class BlockProxy implements Comparable {
+
+ private String number;
+ @Expose(deserialize = false, serialize = false)
+ private Long _number;
+ private String hash;
+ private String parentHash;
+ private String stateRoot;
+ private String size;
+ @Expose(deserialize = false, serialize = false)
+ private Long _size;
+ private String difficulty;
+ private String totalDifficulty;
+ private String timestamp;
+ @Expose(deserialize = false, serialize = false)
+ private LocalDateTime _timestamp;
+
+ private String miner;
+ private String nonce;
+ private String extraData;
+ private String logsBloom;
+ private String mixHash;
+ private String gasUsed;
+ @Expose(deserialize = false, serialize = false)
+ private Wei _gasUsed;
+ private String gasLimit;
+ @Expose(deserialize = false, serialize = false)
+ private Wei _gasLimit;
+
+ private String sha3Uncles;
+ private List uncles;
+
+ private String receiptsRoot;
+ private String transactionsRoot;
+ private List transactions;
+
+ protected BlockProxy() {}
+
+ //
+ public Long getNumber() {
+ if (_number == null && !BasicUtils.isEmpty(number))
+ _number = BasicUtils.parseHex(number).longValue();
+ return _number;
+ }
+
+ public String getHash() {
+ return hash;
+ }
+
+ public String getParentHash() {
+ return parentHash;
+ }
+
+ public String getStateRoot() {
+ return stateRoot;
+ }
+
+ public Long getSize() {
+ if (_size == null && !BasicUtils.isEmpty(size))
+ _size = BasicUtils.parseHex(size).longValue();
+ return _size;
+ }
+
+ public String getDifficulty() {
+ return difficulty;
+ }
+
+ public String getTotalDifficulty() {
+ return totalDifficulty;
+ }
+
+ public LocalDateTime getTimeStamp() {
+ if (_timestamp == null && !BasicUtils.isEmpty(timestamp))
+ _timestamp = LocalDateTime.ofEpochSecond(BasicUtils.parseHex(timestamp).longValue(), 0, ZoneOffset.UTC);
+ return _timestamp;
+ }
+
+ public String getMiner() {
+ return miner;
+ }
+
+ public String getNonce() {
+ return nonce;
+ }
+
+ public String getExtraData() {
+ return extraData;
+ }
+
+ public String getLogsBloom() {
+ return logsBloom;
+ }
+
+ public String getMixHash() {
+ return mixHash;
+ }
+
+ public Wei getGasUsed() {
+ if (_gasUsed == null && !BasicUtils.isEmpty(gasUsed))
+ _gasUsed = Wei.ofWei(BasicUtils.parseHex(gasUsed));
+ return _gasUsed;
+ }
+
+ public Wei getGasLimit() {
+ if (_gasLimit == null && !BasicUtils.isEmpty(gasLimit))
+ _gasLimit = Wei.ofWei(BasicUtils.parseHex(gasLimit));
+ return _gasLimit;
+ }
+
+ public String getSha3Uncles() {
+ return sha3Uncles;
+ }
+
+ public List getUncles() {
+ return uncles;
+ }
+
+ public String getReceiptsRoot() {
+ return receiptsRoot;
+ }
+
+ public String getTransactionsRoot() {
+ return transactionsRoot;
+ }
+
+ public List getTransactions() {
+ return transactions;
+ }
+ //
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (!(o instanceof BlockProxy))
+ return false;
+ BlockProxy that = (BlockProxy) o;
+ return Objects.equals(number, that.number) && Objects.equals(hash, that.hash)
+ && Objects.equals(parentHash, that.parentHash) && Objects.equals(nonce, that.nonce);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(number, hash, parentHash, nonce);
+ }
+
+ @Override
+ public String toString() {
+ return "BlockProxy{" +
+ "number='" + number + '\'' +
+ ", hash='" + hash + '\'' +
+ ", parentHash='" + parentHash + '\'' +
+ ", stateRoot='" + stateRoot + '\'' +
+ ", size='" + size + '\'' +
+ ", difficulty='" + difficulty + '\'' +
+ ", totalDifficulty='" + totalDifficulty + '\'' +
+ ", timestamp='" + timestamp + '\'' +
+ ", miner='" + miner + '\'' +
+ ", nonce='" + nonce + '\'' +
+ ", extraData='" + extraData + '\'' +
+ ", logsBloom='" + logsBloom + '\'' +
+ ", mixHash='" + mixHash + '\'' +
+ ", gasUsed='" + gasUsed + '\'' +
+ ", gasLimit='" + gasLimit + '\'' +
+ ", sha3Uncles='" + sha3Uncles + '\'' +
+ ", uncles=" + uncles +
+ ", receiptsRoot='" + receiptsRoot + '\'' +
+ ", transactionsRoot='" + transactionsRoot + '\'' +
+ ", transactions=" + transactions +
+ '}';
+ }
+
+ @Override
+ public int compareTo(@NotNull BlockProxy o) {
+ return Long.compare(getNumber(), o.getNumber());
+ }
+
+ public static BlockProxyBuilder builder() {
+ return new BlockProxyBuilder();
+ }
+
+ public static final class BlockProxyBuilder {
+
+ private Long number;
+ private String hash;
+ private String parentHash;
+ private String stateRoot;
+ private Long size;
+ private String difficulty;
+ private String totalDifficulty;
+ private LocalDateTime timestamp;
+ private String miner;
+ private String nonce;
+ private String extraData;
+ private String logsBloom;
+ private String mixHash;
+ private Wei gasUsed;
+ private Wei gasLimit;
+ private String sha3Uncles;
+ private List uncles;
+ private String receiptsRoot;
+ private String transactionsRoot;
+ private List transactions;
+
+ private BlockProxyBuilder() {}
+
+ public BlockProxyBuilder withNumber(Long number) {
+ this.number = number;
+ return this;
+ }
+
+ public BlockProxyBuilder withHash(String hash) {
+ this.hash = hash;
+ return this;
+ }
+
+ public BlockProxyBuilder withParentHash(String parentHash) {
+ this.parentHash = parentHash;
+ return this;
+ }
+
+ public BlockProxyBuilder withStateRoot(String stateRoot) {
+ this.stateRoot = stateRoot;
+ return this;
+ }
+
+ public BlockProxyBuilder withSize(Long size) {
+ this.size = size;
+ return this;
+ }
+
+ public BlockProxyBuilder withDifficulty(String difficulty) {
+ this.difficulty = difficulty;
+ return this;
+ }
+
+ public BlockProxyBuilder withTotalDifficulty(String totalDifficulty) {
+ this.totalDifficulty = totalDifficulty;
+ return this;
+ }
+
+ public BlockProxyBuilder withTimestamp(LocalDateTime timestamp) {
+ this.timestamp = timestamp;
+ return this;
+ }
+
+ public BlockProxyBuilder withMiner(String miner) {
+ this.miner = miner;
+ return this;
+ }
+
+ public BlockProxyBuilder withNonce(String nonce) {
+ this.nonce = nonce;
+ return this;
+ }
+
+ public BlockProxyBuilder withExtraData(String extraData) {
+ this.extraData = extraData;
+ return this;
+ }
+
+ public BlockProxyBuilder withLogsBloom(String logsBloom) {
+ this.logsBloom = logsBloom;
+ return this;
+ }
+
+ public BlockProxyBuilder withMixHash(String mixHash) {
+ this.mixHash = mixHash;
+ return this;
+ }
+
+ public BlockProxyBuilder withGasUsed(Wei gasUsed) {
+ this.gasUsed = gasUsed;
+ return this;
+ }
+
+ public BlockProxyBuilder withGasLimit(Wei gasLimit) {
+ this.gasLimit = gasLimit;
+ return this;
+ }
+
+ public BlockProxyBuilder withSha3Uncles(String sha3Uncles) {
+ this.sha3Uncles = sha3Uncles;
+ return this;
+ }
+
+ public BlockProxyBuilder withUncles(List uncles) {
+ this.uncles = uncles;
+ return this;
+ }
+
+ public BlockProxyBuilder withReceiptsRoot(String receiptsRoot) {
+ this.receiptsRoot = receiptsRoot;
+ return this;
+ }
+
+ public BlockProxyBuilder withTransactionsRoot(String transactionsRoot) {
+ this.transactionsRoot = transactionsRoot;
+ return this;
+ }
+
+ public BlockProxyBuilder withTransactions(List transactions) {
+ this.transactions = transactions;
+ return this;
+ }
+
+ public BlockProxy build() {
+ BlockProxy blockProxy = new BlockProxy();
+ blockProxy.mixHash = this.mixHash;
+ blockProxy.totalDifficulty = this.totalDifficulty;
+ blockProxy.nonce = this.nonce;
+ blockProxy.uncles = this.uncles;
+ blockProxy.transactionsRoot = this.transactionsRoot;
+ blockProxy.number = String.valueOf(this.number);
+ blockProxy.logsBloom = this.logsBloom;
+ blockProxy.receiptsRoot = this.receiptsRoot;
+ blockProxy.hash = this.hash;
+ blockProxy.parentHash = this.parentHash;
+ blockProxy._size = this.size;
+ blockProxy.difficulty = this.difficulty;
+ if (this.gasLimit != null) {
+ blockProxy._gasLimit = this.gasLimit;
+ }
+ if (this.gasUsed != null) {
+ blockProxy._gasUsed = this.gasUsed;
+ }
+ blockProxy.size = String.valueOf(this.size);
+ blockProxy.extraData = this.extraData;
+ blockProxy.stateRoot = this.stateRoot;
+ blockProxy.sha3Uncles = this.sha3Uncles;
+ blockProxy.miner = this.miner;
+ if (this.timestamp != null) {
+ blockProxy.timestamp = String.valueOf(this.timestamp.toEpochSecond(ZoneOffset.UTC));
+ blockProxy._timestamp = this.timestamp;
+ }
+ blockProxy.transactions = this.transactions;
+ blockProxy._number = this.number;
+ return blockProxy;
+ }
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java
new file mode 100644
index 0000000..e6df01c
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java
@@ -0,0 +1,237 @@
+package io.goodforgod.api.etherscan.model.proxy;
+
+import com.google.gson.annotations.Expose;
+import io.goodforgod.api.etherscan.model.Log;
+import io.goodforgod.api.etherscan.model.Wei;
+import io.goodforgod.api.etherscan.util.BasicUtils;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * @author GoodforGod
+ * @since 03.11.2018
+ */
+public class ReceiptProxy {
+
+ private String root;
+ private String from;
+ private String to;
+ private String blockNumber;
+ @Expose(serialize = false, deserialize = false)
+ private Long _blockNumber;
+ private String blockHash;
+ private String transactionHash;
+ private String transactionIndex;
+ @Expose(serialize = false, deserialize = false)
+ private Long _transactionIndex;
+ private String gasUsed;
+ @Expose(serialize = false, deserialize = false)
+ private Wei _gasUsed;
+ private String cumulativeGasUsed;
+ @Expose(serialize = false, deserialize = false)
+ private Wei _cumulativeGasUsed;
+ private String contractAddress;
+
+ private List logs;
+ private String logsBloom;
+
+ protected ReceiptProxy() {}
+
+ //
+ public String getRoot() {
+ return root;
+ }
+
+ public String getFrom() {
+ return from;
+ }
+
+ public String getTo() {
+ return to;
+ }
+
+ public Long getBlockNumber() {
+ if (_blockNumber == null && !BasicUtils.isEmpty(blockNumber))
+ _blockNumber = BasicUtils.parseHex(blockNumber).longValue();
+ return _blockNumber;
+ }
+
+ public String getBlockHash() {
+ return blockHash;
+ }
+
+ public String getTransactionHash() {
+ return transactionHash;
+ }
+
+ public Long getTransactionIndex() {
+ if (_transactionIndex == null && !BasicUtils.isEmpty(transactionIndex))
+ _transactionIndex = BasicUtils.parseHex(transactionIndex).longValue();
+ return _transactionIndex;
+ }
+
+ public Wei getGasUsed() {
+ if (_gasUsed == null && !BasicUtils.isEmpty(gasUsed))
+ _gasUsed = Wei.ofWei(BasicUtils.parseHex(gasUsed));
+ return _gasUsed;
+ }
+
+ public Wei getGasUsedCumulative() {
+ if (_cumulativeGasUsed == null && !BasicUtils.isEmpty(cumulativeGasUsed))
+ _cumulativeGasUsed = Wei.ofWei(BasicUtils.parseHex(cumulativeGasUsed));
+ return _cumulativeGasUsed;
+ }
+
+ public String getContractAddress() {
+ return contractAddress;
+ }
+
+ public List getLogs() {
+ return logs;
+ }
+
+ public String getLogsBloom() {
+ return logsBloom;
+ }
+ //
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (!(o instanceof ReceiptProxy))
+ return false;
+ ReceiptProxy that = (ReceiptProxy) o;
+ return Objects.equals(blockNumber, that.blockNumber) && Objects.equals(blockHash, that.blockHash)
+ && Objects.equals(transactionHash, that.transactionHash)
+ && Objects.equals(transactionIndex, that.transactionIndex);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(blockNumber, blockHash, transactionHash, transactionIndex);
+ }
+
+ @Override
+ public String toString() {
+ return "ReceiptProxy{" +
+ "root='" + root + '\'' +
+ ", from='" + from + '\'' +
+ ", to='" + to + '\'' +
+ ", blockNumber='" + blockNumber + '\'' +
+ ", blockHash='" + blockHash + '\'' +
+ ", transactionHash='" + transactionHash + '\'' +
+ ", transactionIndex='" + transactionIndex + '\'' +
+ ", gasUsed='" + gasUsed + '\'' +
+ ", cumulativeGasUsed='" + cumulativeGasUsed + '\'' +
+ ", contractAddress='" + contractAddress + '\'' +
+ ", logs=" + logs +
+ ", logsBloom='" + logsBloom + '\'' +
+ '}';
+ }
+
+ public static ReceiptProxyBuilder builder() {
+ return new ReceiptProxyBuilder();
+ }
+
+ public static final class ReceiptProxyBuilder {
+
+ private String root;
+ private String from;
+ private String to;
+ private Long blockNumber;
+ private String blockHash;
+ private String transactionHash;
+ private Long transactionIndex;
+ private Wei gasUsed;
+ private Wei cumulativeGasUsed;
+ private String contractAddress;
+ private List logs;
+ private String logsBloom;
+
+ private ReceiptProxyBuilder() {}
+
+ public ReceiptProxyBuilder withRoot(String root) {
+ this.root = root;
+ return this;
+ }
+
+ public ReceiptProxyBuilder withFrom(String from) {
+ this.from = from;
+ return this;
+ }
+
+ public ReceiptProxyBuilder withTo(String to) {
+ this.to = to;
+ return this;
+ }
+
+ public ReceiptProxyBuilder withBlockNumber(Long blockNumber) {
+ this.blockNumber = blockNumber;
+ return this;
+ }
+
+ public ReceiptProxyBuilder withBlockHash(String blockHash) {
+ this.blockHash = blockHash;
+ return this;
+ }
+
+ public ReceiptProxyBuilder withTransactionHash(String transactionHash) {
+ this.transactionHash = transactionHash;
+ return this;
+ }
+
+ public ReceiptProxyBuilder withTransactionIndex(Long transactionIndex) {
+ this.transactionIndex = transactionIndex;
+ return this;
+ }
+
+ public ReceiptProxyBuilder withGasUsed(Wei gasUsed) {
+ this.gasUsed = gasUsed;
+ return this;
+ }
+
+ public ReceiptProxyBuilder withCumulativeGasUsed(Wei cumulativeGasUsed) {
+ this.cumulativeGasUsed = cumulativeGasUsed;
+ return this;
+ }
+
+ public ReceiptProxyBuilder withContractAddress(String contractAddress) {
+ this.contractAddress = contractAddress;
+ return this;
+ }
+
+ public ReceiptProxyBuilder withLogs(List logs) {
+ this.logs = logs;
+ return this;
+ }
+
+ public ReceiptProxyBuilder withLogsBloom(String logsBloom) {
+ this.logsBloom = logsBloom;
+ return this;
+ }
+
+ public ReceiptProxy build() {
+ ReceiptProxy receiptProxy = new ReceiptProxy();
+ receiptProxy.logsBloom = this.logsBloom;
+ receiptProxy.transactionHash = this.transactionHash;
+ receiptProxy.blockNumber = String.valueOf(this.blockNumber);
+ receiptProxy.from = this.from;
+ receiptProxy._transactionIndex = this.transactionIndex;
+ receiptProxy.blockHash = this.blockHash;
+ receiptProxy.root = this.root;
+ receiptProxy.contractAddress = this.contractAddress;
+ if (this.gasUsed != null) {
+ receiptProxy._gasUsed = this.gasUsed;
+ }
+ receiptProxy.logs = this.logs;
+ receiptProxy.to = this.to;
+ if (this.cumulativeGasUsed != null) {
+ receiptProxy._cumulativeGasUsed = this.cumulativeGasUsed;
+ }
+ receiptProxy.transactionIndex = String.valueOf(this.transactionIndex);
+ receiptProxy._blockNumber = this.blockNumber;
+ return receiptProxy;
+ }
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java
new file mode 100644
index 0000000..70b4fd7
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java
@@ -0,0 +1,274 @@
+package io.goodforgod.api.etherscan.model.proxy;
+
+import com.google.gson.annotations.Expose;
+import io.goodforgod.api.etherscan.model.Wei;
+import io.goodforgod.api.etherscan.util.BasicUtils;
+import java.util.Objects;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author GoodforGod
+ * @since 31.10.2018
+ */
+public class TxProxy implements Comparable {
+
+ private String to;
+ private String hash;
+ private String transactionIndex;
+ @Expose(deserialize = false, serialize = false)
+ private Long _transactionIndex;
+ private String from;
+ private String v;
+ private String input;
+ private String s;
+ private String r;
+ private String nonce;
+ @Expose(deserialize = false, serialize = false)
+ private Long _nonce;
+ private String value;
+ private String gas;
+ @Expose(deserialize = false, serialize = false)
+ private Wei _gas;
+ private String gasPrice;
+ @Expose(deserialize = false, serialize = false)
+ private Wei _gasPrice;
+ private String blockHash;
+ private String blockNumber;
+ @Expose(deserialize = false, serialize = false)
+ private Long _blockNumber;
+
+ protected TxProxy() {}
+
+ //
+ public String getTo() {
+ return to;
+ }
+
+ public String getHash() {
+ return hash;
+ }
+
+ public Long getTransactionIndex() {
+ if (_transactionIndex == null && !BasicUtils.isEmpty(transactionIndex))
+ _transactionIndex = BasicUtils.parseHex(transactionIndex).longValue();
+ return _transactionIndex;
+ }
+
+ public String getFrom() {
+ return from;
+ }
+
+ public Wei getGas() {
+ if (_gas == null && !BasicUtils.isEmpty(gas))
+ _gas = Wei.ofWei(BasicUtils.parseHex(gas));
+ return _gas;
+ }
+
+ public String getV() {
+ return v;
+ }
+
+ public String getInput() {
+ return input;
+ }
+
+ public String getS() {
+ return s;
+ }
+
+ public String getR() {
+ return r;
+ }
+
+ public Long getNonce() {
+ if (_nonce == null && !BasicUtils.isEmpty(nonce))
+ _nonce = BasicUtils.parseHex(nonce).longValue();
+ return _nonce;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public Wei getGasPrice() {
+ if (_gasPrice == null && !BasicUtils.isEmpty(gasPrice))
+ _gasPrice = Wei.ofWei(BasicUtils.parseHex(gasPrice));
+ return _gasPrice;
+ }
+
+ public String getBlockHash() {
+ return blockHash;
+ }
+
+ public Long getBlockNumber() {
+ if (_blockNumber == null && !BasicUtils.isEmpty(blockNumber))
+ _blockNumber = BasicUtils.parseHex(blockNumber).longValue();
+ return _blockNumber;
+ }
+ //
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+ if (!(o instanceof TxProxy))
+ return false;
+ TxProxy txProxy = (TxProxy) o;
+ return Objects.equals(hash, txProxy.hash) && Objects.equals(transactionIndex, txProxy.transactionIndex)
+ && Objects.equals(nonce, txProxy.nonce) && Objects.equals(blockHash, txProxy.blockHash)
+ && Objects.equals(blockNumber, txProxy.blockNumber);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(hash, transactionIndex, nonce, blockHash, blockNumber);
+ }
+
+ @Override
+ public String toString() {
+ return "TxProxy{" +
+ "to='" + to + '\'' +
+ ", hash='" + hash + '\'' +
+ ", transactionIndex='" + transactionIndex + '\'' +
+ ", from='" + from + '\'' +
+ ", v='" + v + '\'' +
+ ", input='" + input + '\'' +
+ ", s='" + s + '\'' +
+ ", r='" + r + '\'' +
+ ", nonce='" + nonce + '\'' +
+ ", value='" + value + '\'' +
+ ", gas='" + gas + '\'' +
+ ", gasPrice='" + gasPrice + '\'' +
+ ", blockHash='" + blockHash + '\'' +
+ ", blockNumber='" + blockNumber + '\'' +
+ '}';
+ }
+
+ @Override
+ public int compareTo(@NotNull TxProxy o) {
+ final int firstCompare = Long.compare(getBlockNumber(), o.getBlockNumber());
+ return (firstCompare == 0)
+ ? Long.compare(getTransactionIndex(), o.getTransactionIndex())
+ : firstCompare;
+ }
+
+ public static TxProxyBuilder builder() {
+ return new TxProxyBuilder();
+ }
+
+ public static final class TxProxyBuilder {
+
+ private String to;
+ private String hash;
+ private Long transactionIndex;
+ private String from;
+ private String v;
+ private String input;
+ private String s;
+ private String r;
+ private Long nonce;
+ private String value;
+ private Wei gas;
+ private Wei gasPrice;
+ private String blockHash;
+ private Long blockNumber;
+
+ private TxProxyBuilder() {}
+
+ public TxProxyBuilder withTo(String to) {
+ this.to = to;
+ return this;
+ }
+
+ public TxProxyBuilder withHash(String hash) {
+ this.hash = hash;
+ return this;
+ }
+
+ public TxProxyBuilder withTransactionIndex(Long transactionIndex) {
+ this.transactionIndex = transactionIndex;
+ return this;
+ }
+
+ public TxProxyBuilder withFrom(String from) {
+ this.from = from;
+ return this;
+ }
+
+ public TxProxyBuilder withV(String v) {
+ this.v = v;
+ return this;
+ }
+
+ public TxProxyBuilder withInput(String input) {
+ this.input = input;
+ return this;
+ }
+
+ public TxProxyBuilder withS(String s) {
+ this.s = s;
+ return this;
+ }
+
+ public TxProxyBuilder withR(String r) {
+ this.r = r;
+ return this;
+ }
+
+ public TxProxyBuilder withNonce(Long nonce) {
+ this.nonce = nonce;
+ return this;
+ }
+
+ public TxProxyBuilder withValue(String value) {
+ this.value = value;
+ return this;
+ }
+
+ public TxProxyBuilder withGas(Wei gas) {
+ this.gas = gas;
+ return this;
+ }
+
+ public TxProxyBuilder withGasPrice(Wei gasPrice) {
+ this.gasPrice = gasPrice;
+ return this;
+ }
+
+ public TxProxyBuilder withBlockHash(String blockHash) {
+ this.blockHash = blockHash;
+ return this;
+ }
+
+ public TxProxyBuilder withBlockNumber(Long blockNumber) {
+ this.blockNumber = blockNumber;
+ return this;
+ }
+
+ public TxProxy build() {
+ TxProxy txProxy = new TxProxy();
+ txProxy.input = this.input;
+ if (this.gas != null) {
+ txProxy._gas = this.gas;
+ }
+ txProxy.s = this.s;
+ txProxy.blockHash = this.blockHash;
+ txProxy.to = this.to;
+ txProxy.r = this.r;
+ txProxy.transactionIndex = String.valueOf(this.transactionIndex);
+ txProxy._nonce = this.nonce;
+ txProxy.value = this.value;
+ txProxy.v = this.v;
+ txProxy.from = this.from;
+ txProxy.nonce = String.valueOf(this.nonce);
+ txProxy._transactionIndex = this.transactionIndex;
+ txProxy.blockNumber = String.valueOf(this.blockNumber);
+ txProxy._blockNumber = this.blockNumber;
+ txProxy.hash = this.hash;
+ if (this.gasPrice != null) {
+ txProxy._gasPrice = this.gasPrice;
+ }
+ return txProxy;
+ }
+ }
+}
diff --git a/src/main/java/io/api/etherscan/model/proxy/utility/BaseProxyTO.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BaseProxyTO.java
similarity index 83%
rename from src/main/java/io/api/etherscan/model/proxy/utility/BaseProxyTO.java
rename to src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BaseProxyTO.java
index 52c886f..ef57193 100644
--- a/src/main/java/io/api/etherscan/model/proxy/utility/BaseProxyTO.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BaseProxyTO.java
@@ -1,8 +1,6 @@
-package io.api.etherscan.model.proxy.utility;
+package io.goodforgod.api.etherscan.model.proxy.utility;
/**
- * ! NO DESCRIPTION !
- *
* @author GoodforGod
* @since 31.10.2018
*/
diff --git a/src/main/java/io/api/etherscan/model/proxy/utility/BlockProxyTO.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BlockProxyTO.java
similarity index 62%
rename from src/main/java/io/api/etherscan/model/proxy/utility/BlockProxyTO.java
rename to src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BlockProxyTO.java
index eb9d941..cf6c16b 100644
--- a/src/main/java/io/api/etherscan/model/proxy/utility/BlockProxyTO.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BlockProxyTO.java
@@ -1,10 +1,8 @@
-package io.api.etherscan.model.proxy.utility;
+package io.goodforgod.api.etherscan.model.proxy.utility;
-import io.api.etherscan.model.proxy.BlockProxy;
+import io.goodforgod.api.etherscan.model.proxy.BlockProxy;
/**
- * ! NO DESCRIPTION !
- *
* @author GoodforGod
* @since 01.11.2018
*/
diff --git a/src/main/java/io/api/etherscan/model/proxy/utility/ErrorProxyTO.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/ErrorProxyTO.java
similarity index 78%
rename from src/main/java/io/api/etherscan/model/proxy/utility/ErrorProxyTO.java
rename to src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/ErrorProxyTO.java
index 57d2c07..9b14cec 100644
--- a/src/main/java/io/api/etherscan/model/proxy/utility/ErrorProxyTO.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/ErrorProxyTO.java
@@ -1,8 +1,6 @@
-package io.api.etherscan.model.proxy.utility;
+package io.goodforgod.api.etherscan.model.proxy.utility;
/**
- * ! NO DESCRIPTION !
- *
* @author GoodforGod
* @since 03.11.2018
*/
diff --git a/src/main/java/io/api/etherscan/model/proxy/utility/StringProxyTO.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/StringProxyTO.java
similarity index 73%
rename from src/main/java/io/api/etherscan/model/proxy/utility/StringProxyTO.java
rename to src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/StringProxyTO.java
index 90cd7c8..489d87b 100644
--- a/src/main/java/io/api/etherscan/model/proxy/utility/StringProxyTO.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/StringProxyTO.java
@@ -1,8 +1,6 @@
-package io.api.etherscan.model.proxy.utility;
+package io.goodforgod.api.etherscan.model.proxy.utility;
/**
- * ! NO DESCRIPTION !
- *
* @author GoodforGod
* @since 31.10.2018
*/
diff --git a/src/main/java/io/api/etherscan/model/proxy/utility/TxInfoProxyTO.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxInfoProxyTO.java
similarity index 62%
rename from src/main/java/io/api/etherscan/model/proxy/utility/TxInfoProxyTO.java
rename to src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxInfoProxyTO.java
index c709f76..208cdbe 100644
--- a/src/main/java/io/api/etherscan/model/proxy/utility/TxInfoProxyTO.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxInfoProxyTO.java
@@ -1,10 +1,8 @@
-package io.api.etherscan.model.proxy.utility;
+package io.goodforgod.api.etherscan.model.proxy.utility;
-import io.api.etherscan.model.proxy.ReceiptProxy;
+import io.goodforgod.api.etherscan.model.proxy.ReceiptProxy;
/**
- * ! NO DESCRIPTION !
- *
* @author GoodforGod
* @since 03.11.2018
*/
diff --git a/src/main/java/io/api/etherscan/model/proxy/utility/TxProxyTO.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxProxyTO.java
similarity index 62%
rename from src/main/java/io/api/etherscan/model/proxy/utility/TxProxyTO.java
rename to src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxProxyTO.java
index 4140a62..0c084e7 100644
--- a/src/main/java/io/api/etherscan/model/proxy/utility/TxProxyTO.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxProxyTO.java
@@ -1,10 +1,8 @@
-package io.api.etherscan.model.proxy.utility;
+package io.goodforgod.api.etherscan.model.proxy.utility;
-import io.api.etherscan.model.proxy.TxProxy;
+import io.goodforgod.api.etherscan.model.proxy.TxProxy;
/**
- * ! NO DESCRIPTION !
- *
* @author GoodforGod
* @since 01.11.2018
*/
diff --git a/src/main/java/io/api/etherscan/model/query/LogOp.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogOp.java
similarity index 86%
rename from src/main/java/io/api/etherscan/model/query/LogOp.java
rename to src/main/java/io/goodforgod/api/etherscan/model/query/LogOp.java
index 0c0ebee..9136034 100644
--- a/src/main/java/io/api/etherscan/model/query/LogOp.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogOp.java
@@ -1,4 +1,4 @@
-package io.api.etherscan.model.query;
+package io.goodforgod.api.etherscan.model.query;
/**
* Part of The Event Log API
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/query/LogQuery.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQuery.java
new file mode 100644
index 0000000..9d8ea5a
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQuery.java
@@ -0,0 +1,51 @@
+package io.goodforgod.api.etherscan.model.query;
+
+import static io.goodforgod.api.etherscan.model.query.LogQueryBuilderImpl.MAX_BLOCK;
+import static io.goodforgod.api.etherscan.model.query.LogQueryBuilderImpl.MIN_BLOCK;
+
+import io.goodforgod.api.etherscan.LogsAPI;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Final built container for The Event Log API
+ * EtherScan - API Descriptions ...
+ *
+ * @see LogQueryBuilderImpl
+ * @see LogsAPI
+ * @author GoodforGod
+ * @since 10.05.2023
+ */
+public interface LogQuery {
+
+ @NotNull
+ String params();
+
+ @NotNull
+ static Builder builder(@NotNull String address) {
+ return new LogQueryBuilderImpl(address, MIN_BLOCK, MAX_BLOCK);
+ }
+
+ interface Builder {
+
+ @NotNull
+ LogQuery.Builder withBlockFrom(long startBlock);
+
+ @NotNull
+ LogQuery.Builder withBlockTo(long endBlock);
+
+ @NotNull
+ LogTopicSingle withTopic(@NotNull String topic0);
+
+ @NotNull
+ LogTopicTuple withTopic(@NotNull String topic0, @NotNull String topic1);
+
+ @NotNull
+ LogTopicTriple withTopic(@NotNull String topic0, @NotNull String topic1, @NotNull String topic2);
+
+ @NotNull
+ LogTopicQuadro withTopic(@NotNull String topic0, @NotNull String topic1, @NotNull String topic2, @NotNull String topic3);
+
+ @NotNull
+ LogQuery build();
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryBuilderImpl.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryBuilderImpl.java
new file mode 100644
index 0000000..549bd47
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryBuilderImpl.java
@@ -0,0 +1,86 @@
+package io.goodforgod.api.etherscan.model.query;
+
+import io.goodforgod.api.etherscan.LogsAPI;
+import io.goodforgod.api.etherscan.error.EtherScanLogQueryException;
+import io.goodforgod.api.etherscan.util.BasicUtils;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Builder for The Event Log API
+ *
+ * @see LogsAPI
+ * @author GoodforGod
+ * @since 31.10.2018
+ */
+final class LogQueryBuilderImpl implements LogQuery.Builder {
+
+ static final long MIN_BLOCK = 0;
+ static final long MAX_BLOCK = 99999999999999999L;
+
+ private final String address;
+ private final long startBlock, endBlock;
+
+ LogQueryBuilderImpl(String address, long startBlock, long endBlock) {
+ BasicUtils.validateAddress(address);
+ this.address = address;
+ this.startBlock = startBlock;
+ this.endBlock = endBlock;
+ }
+
+ @Override
+ public @NotNull LogQuery.Builder withBlockFrom(long startBlock) {
+ return new LogQueryBuilderImpl(this.address, startBlock, this.endBlock);
+ }
+
+ @Override
+ public @NotNull LogQuery.Builder withBlockTo(long endBlock) {
+ return new LogQueryBuilderImpl(this.address, this.startBlock, endBlock);
+ }
+
+ @Override
+ public @NotNull LogTopicSingle withTopic(@NotNull String topic0) {
+ if (BasicUtils.isNotHex(topic0))
+ throw new EtherScanLogQueryException("topic0 can not be empty or non hex.");
+ return new LogTopicSingle(address, startBlock, endBlock, topic0);
+ }
+
+ @Override
+ public @NotNull LogTopicTuple withTopic(@NotNull String topic0, @NotNull String topic1) {
+ if (BasicUtils.isNotHex(topic0))
+ throw new EtherScanLogQueryException("topic0 can not be empty or non hex.");
+ if (BasicUtils.isNotHex(topic1))
+ throw new EtherScanLogQueryException("topic1 can not be empty or non hex.");
+ return new LogTopicTuple(address, startBlock, endBlock, topic0, topic1);
+ }
+
+ @Override
+ public @NotNull LogTopicTriple withTopic(@NotNull String topic0, @NotNull String topic1, @NotNull String topic2) {
+ if (BasicUtils.isNotHex(topic0))
+ throw new EtherScanLogQueryException("topic0 can not be empty or non hex.");
+ if (BasicUtils.isNotHex(topic1))
+ throw new EtherScanLogQueryException("topic1 can not be empty or non hex.");
+ if (BasicUtils.isNotHex(topic2))
+ throw new EtherScanLogQueryException("topic2 can not be empty or non hex.");
+ return new LogTopicTriple(address, startBlock, endBlock, topic0, topic1, topic2);
+ }
+
+ @Override
+ public @NotNull LogTopicQuadro
+ withTopic(@NotNull String topic0, @NotNull String topic1, @NotNull String topic2, @NotNull String topic3) {
+ if (BasicUtils.isNotHex(topic0))
+ throw new EtherScanLogQueryException("topic0 can not be empty or non hex.");
+ if (BasicUtils.isNotHex(topic1))
+ throw new EtherScanLogQueryException("topic1 can not be empty or non hex.");
+ if (BasicUtils.isNotHex(topic2))
+ throw new EtherScanLogQueryException("topic2 can not be empty or non hex.");
+ if (BasicUtils.isNotHex(topic3))
+ throw new EtherScanLogQueryException("topic3 can not be empty or non hex.");
+
+ return new LogTopicQuadro(address, startBlock, endBlock, topic0, topic1, topic2, topic3);
+ }
+
+ @Override
+ public @NotNull LogQuery build() throws EtherScanLogQueryException {
+ return new LogQueryImpl("&address=" + this.address + "&fromBlock=" + this.startBlock + "&toBlock=" + this.endBlock);
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryImpl.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryImpl.java
new file mode 100644
index 0000000..ef66e72
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryImpl.java
@@ -0,0 +1,30 @@
+package io.goodforgod.api.etherscan.model.query;
+
+import io.goodforgod.api.etherscan.LogsAPI;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Final builded container for The Event Log API
+ * EtherScan - API Descriptions ...
+ *
+ * @see LogQueryBuilderImpl
+ * @see LogsAPI
+ * @author GoodforGod
+ * @since 31.10.2018
+ */
+final class LogQueryImpl implements LogQuery {
+
+ /**
+ * Final request parameter for api call
+ */
+ private final String params;
+
+ LogQueryImpl(String params) {
+ this.params = params;
+ }
+
+ @Override
+ public @NotNull String params() {
+ return params;
+ }
+}
diff --git a/src/main/java/io/api/etherscan/model/query/impl/BaseLogQuery.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryParams.java
similarity index 79%
rename from src/main/java/io/api/etherscan/model/query/impl/BaseLogQuery.java
rename to src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryParams.java
index 2fc688a..ac77ae8 100644
--- a/src/main/java/io/api/etherscan/model/query/impl/BaseLogQuery.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryParams.java
@@ -1,17 +1,18 @@
-package io.api.etherscan.model.query.impl;
+package io.goodforgod.api.etherscan.model.query;
-import io.api.etherscan.core.ILogsApi;
+import io.goodforgod.api.etherscan.LogsAPI;
/**
* Base parameters for The Event Log API builder
*
- * @see LogQueryBuilder
- * @see ILogsApi
- *
+ * @see LogQueryBuilderImpl
+ * @see LogsAPI
* @author GoodforGod
* @since 31.10.2018
*/
-abstract class BaseLogQuery {
+final class LogQueryParams {
+
+ private LogQueryParams() {}
static final String FROM_BLOCK_PARAM = "&fromBlock=";
static final String TO_BLOCK_PARAM = "&toBlock=";
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicBuilder.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicBuilder.java
new file mode 100644
index 0000000..715f869
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicBuilder.java
@@ -0,0 +1,17 @@
+package io.goodforgod.api.etherscan.model.query;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @see LogTopicSingle
+ * @see LogTopicTuple
+ * @see LogTopicTriple
+ * @see LogTopicQuadro
+ * @author GoodforGod
+ * @since 10.05.2023
+ */
+public interface LogTopicBuilder {
+
+ @NotNull
+ LogQuery build();
+}
diff --git a/src/main/java/io/api/etherscan/model/query/impl/LogTopicQuadro.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicQuadro.java
similarity index 64%
rename from src/main/java/io/api/etherscan/model/query/impl/LogTopicQuadro.java
rename to src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicQuadro.java
index 1c2bf35..7fdd9db 100644
--- a/src/main/java/io/api/etherscan/model/query/impl/LogTopicQuadro.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicQuadro.java
@@ -1,20 +1,20 @@
-package io.api.etherscan.model.query.impl;
+package io.goodforgod.api.etherscan.model.query;
-import io.api.etherscan.core.ILogsApi;
-import io.api.etherscan.error.LogQueryException;
-import io.api.etherscan.model.query.IQueryBuilder;
-import io.api.etherscan.model.query.LogOp;
+import static io.goodforgod.api.etherscan.model.query.LogQueryParams.*;
+
+import io.goodforgod.api.etherscan.LogsAPI;
+import io.goodforgod.api.etherscan.error.EtherScanLogQueryException;
+import org.jetbrains.annotations.NotNull;
/**
* Quadro topic parameter builder for The Event Log API
*
- * @see LogQueryBuilder
- * @see ILogsApi
- *
+ * @see LogQueryBuilderImpl
+ * @see LogsAPI
* @author GoodforGod
* @since 31.10.2018
*/
-public class LogTopicQuadro extends BaseLogQuery implements IQueryBuilder {
+public final class LogTopicQuadro implements LogTopicBuilder {
private final String address;
private final long startBlock, endBlock;
@@ -22,8 +22,13 @@ public class LogTopicQuadro extends BaseLogQuery implements IQueryBuilder {
private LogOp topic0_1_opr, topic1_2_opr, topic2_3_opr, topic0_2_opr, topic0_3_opr, topic1_3_opr;
- LogTopicQuadro(String address, long startBlock, long endBlock,
- String topic0, String topic1, String topic2, String topic3) {
+ LogTopicQuadro(String address,
+ long startBlock,
+ long endBlock,
+ String topic0,
+ String topic1,
+ String topic2,
+ String topic3) {
this.address = address;
this.startBlock = startBlock;
this.endBlock = endBlock;
@@ -64,21 +69,21 @@ public LogTopicQuadro setOpTopic1_3(LogOp topic1_3_opr) {
}
@Override
- public LogQuery build() {
+ public @NotNull LogQuery build() {
if (topic0_1_opr == null)
- throw new LogQueryException("topic0_1_opr can not be null.");
+ throw new EtherScanLogQueryException("topic0_1_opr can not be null.");
if (topic0_2_opr == null)
- throw new LogQueryException("topic0_2_opr can not be null.");
+ throw new EtherScanLogQueryException("topic0_2_opr can not be null.");
if (topic0_3_opr == null)
- throw new LogQueryException("topic0_3_opr can not be null.");
+ throw new EtherScanLogQueryException("topic0_3_opr can not be null.");
if (topic1_2_opr == null)
- throw new LogQueryException("topic1_2_opr can not be null.");
+ throw new EtherScanLogQueryException("topic1_2_opr can not be null.");
if (topic2_3_opr == null)
- throw new LogQueryException("topic2_3_opr can not be null.");
+ throw new EtherScanLogQueryException("topic2_3_opr can not be null.");
if (topic1_3_opr == null)
- throw new LogQueryException("topic1_3_opr can not be null.");
+ throw new EtherScanLogQueryException("topic1_3_opr can not be null.");
- return new LogQuery(ADDRESS_PARAM + address
+ return new LogQueryImpl(ADDRESS_PARAM + address
+ FROM_BLOCK_PARAM + startBlock + TO_BLOCK_PARAM + endBlock
+ TOPIC_0_PARAM + topic0
+ TOPIC_1_PARAM + topic1
diff --git a/src/main/java/io/api/etherscan/model/query/impl/LogTopicSingle.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicSingle.java
similarity index 53%
rename from src/main/java/io/api/etherscan/model/query/impl/LogTopicSingle.java
rename to src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicSingle.java
index 2c19d61..a736ffa 100644
--- a/src/main/java/io/api/etherscan/model/query/impl/LogTopicSingle.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicSingle.java
@@ -1,19 +1,20 @@
-package io.api.etherscan.model.query.impl;
+package io.goodforgod.api.etherscan.model.query;
-import io.api.etherscan.core.ILogsApi;
-import io.api.etherscan.error.LogQueryException;
-import io.api.etherscan.model.query.IQueryBuilder;
+import static io.goodforgod.api.etherscan.model.query.LogQueryParams.*;
+
+import io.goodforgod.api.etherscan.LogsAPI;
+import io.goodforgod.api.etherscan.error.EtherScanLogQueryException;
+import org.jetbrains.annotations.NotNull;
/**
* Single topic parameter builder for The Event Log API
*
- * @see LogQueryBuilder
- * @see ILogsApi
- *
+ * @see LogQueryBuilderImpl
+ * @see LogsAPI
* @author GoodforGod
* @since 31.10.2018
*/
-public class LogTopicSingle extends BaseLogQuery implements IQueryBuilder {
+public final class LogTopicSingle implements LogTopicBuilder {
private final String address;
private final long startBlock, endBlock;
@@ -28,8 +29,8 @@ public class LogTopicSingle extends BaseLogQuery implements IQueryBuilder {
}
@Override
- public LogQuery build() throws LogQueryException {
- return new LogQuery(ADDRESS_PARAM + address
+ public @NotNull LogQuery build() throws EtherScanLogQueryException {
+ return new LogQueryImpl(ADDRESS_PARAM + address
+ FROM_BLOCK_PARAM + startBlock + TO_BLOCK_PARAM + endBlock
+ TOPIC_0_PARAM + topic0);
}
diff --git a/src/main/java/io/api/etherscan/model/query/impl/LogTopicTriple.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTriple.java
similarity index 59%
rename from src/main/java/io/api/etherscan/model/query/impl/LogTopicTriple.java
rename to src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTriple.java
index aa54740..ac9efb8 100644
--- a/src/main/java/io/api/etherscan/model/query/impl/LogTopicTriple.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTriple.java
@@ -1,20 +1,20 @@
-package io.api.etherscan.model.query.impl;
+package io.goodforgod.api.etherscan.model.query;
-import io.api.etherscan.core.ILogsApi;
-import io.api.etherscan.error.LogQueryException;
-import io.api.etherscan.model.query.IQueryBuilder;
-import io.api.etherscan.model.query.LogOp;
+import static io.goodforgod.api.etherscan.model.query.LogQueryParams.*;
+
+import io.goodforgod.api.etherscan.LogsAPI;
+import io.goodforgod.api.etherscan.error.EtherScanLogQueryException;
+import org.jetbrains.annotations.NotNull;
/**
* Triple topic parameter builder for The Event Log API
*
- * @see LogQueryBuilder
- * @see ILogsApi
- *
+ * @see LogQueryBuilderImpl
+ * @see LogsAPI
* @author GoodforGod
* @since 31.10.2018
*/
-public class LogTopicTriple extends BaseLogQuery implements IQueryBuilder {
+public final class LogTopicTriple implements LogTopicBuilder {
private final String address;
private final long startBlock, endBlock;
@@ -22,8 +22,12 @@ public class LogTopicTriple extends BaseLogQuery implements IQueryBuilder {
private LogOp topic0_1_opr, topic1_2_opr, topic0_2_opr;
- LogTopicTriple(String address, long startBlock, long endBlock,
- String topic0, String topic1, String topic2) {
+ LogTopicTriple(String address,
+ long startBlock,
+ long endBlock,
+ String topic0,
+ String topic1,
+ String topic2) {
this.address = address;
this.startBlock = startBlock;
this.endBlock = endBlock;
@@ -48,15 +52,15 @@ public LogTopicTriple setOpTopic1_2(LogOp topic1_2_opr) {
}
@Override
- public LogQuery build() throws LogQueryException {
+ public @NotNull LogQuery build() throws EtherScanLogQueryException {
if (topic0_1_opr == null)
- throw new LogQueryException("topic0_1_opr can not be null.");
+ throw new EtherScanLogQueryException("topic0_1_opr can not be null.");
if (topic0_2_opr == null)
- throw new LogQueryException("topic0_2_opr can not be null.");
+ throw new EtherScanLogQueryException("topic0_2_opr can not be null.");
if (topic1_2_opr == null)
- throw new LogQueryException("topic1_2_opr can not be null.");
+ throw new EtherScanLogQueryException("topic1_2_opr can not be null.");
- return new LogQuery(ADDRESS_PARAM + address
+ return new LogQueryImpl(ADDRESS_PARAM + address
+ FROM_BLOCK_PARAM + startBlock + TO_BLOCK_PARAM + endBlock
+ TOPIC_0_PARAM + topic0
+ TOPIC_1_PARAM + topic1
diff --git a/src/main/java/io/api/etherscan/model/query/impl/LogTopicTuple.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTuple.java
similarity index 52%
rename from src/main/java/io/api/etherscan/model/query/impl/LogTopicTuple.java
rename to src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTuple.java
index 8f069f1..2ef2bba 100644
--- a/src/main/java/io/api/etherscan/model/query/impl/LogTopicTuple.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTuple.java
@@ -1,20 +1,20 @@
-package io.api.etherscan.model.query.impl;
+package io.goodforgod.api.etherscan.model.query;
-import io.api.etherscan.core.ILogsApi;
-import io.api.etherscan.error.LogQueryException;
-import io.api.etherscan.model.query.IQueryBuilder;
-import io.api.etherscan.model.query.LogOp;
+import static io.goodforgod.api.etherscan.model.query.LogQueryParams.*;
+
+import io.goodforgod.api.etherscan.LogsAPI;
+import io.goodforgod.api.etherscan.error.EtherScanLogQueryException;
+import org.jetbrains.annotations.NotNull;
/**
* Tuple topic parameter builder for The Event Log API
*
- * @see LogQueryBuilder
- * @see ILogsApi
- *
+ * @see LogQueryBuilderImpl
+ * @see LogsAPI
* @author GoodforGod
* @since 31.10.2018
*/
-public class LogTopicTuple extends BaseLogQuery implements IQueryBuilder {
+public final class LogTopicTuple implements LogTopicBuilder {
private final String address;
private final long startBlock, endBlock;
@@ -22,8 +22,11 @@ public class LogTopicTuple extends BaseLogQuery implements IQueryBuilder {
private LogOp topic0_1_opr;
- LogTopicTuple(String address, long startBlock, long endBlock,
- String topic0, String topic1) {
+ LogTopicTuple(String address,
+ long startBlock,
+ long endBlock,
+ String topic0,
+ String topic1) {
this.address = address;
this.startBlock = startBlock;
this.endBlock = endBlock;
@@ -37,11 +40,11 @@ public LogTopicTuple setOpTopic0_1(LogOp topic0_1_opr) {
}
@Override
- public LogQuery build() throws LogQueryException {
+ public @NotNull LogQuery build() throws EtherScanLogQueryException {
if (topic0_1_opr == null)
- throw new LogQueryException("topic0_1_opr can not be null.");
+ throw new EtherScanLogQueryException("topic0_1_opr can not be null.");
- return new LogQuery(ADDRESS_PARAM + address
+ return new LogQueryImpl(ADDRESS_PARAM + address
+ FROM_BLOCK_PARAM + startBlock + TO_BLOCK_PARAM + endBlock
+ TOPIC_0_PARAM + topic0
+ TOPIC_1_PARAM + topic1
diff --git a/src/main/java/io/api/etherscan/model/utility/BalanceResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/BalanceResponseTO.java
similarity index 65%
rename from src/main/java/io/api/etherscan/model/utility/BalanceResponseTO.java
rename to src/main/java/io/goodforgod/api/etherscan/model/response/BalanceResponseTO.java
index 6b23de4..189ed87 100644
--- a/src/main/java/io/api/etherscan/model/utility/BalanceResponseTO.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/response/BalanceResponseTO.java
@@ -1,8 +1,6 @@
-package io.api.etherscan.model.utility;
+package io.goodforgod.api.etherscan.model.response;
/**
- * ! NO DESCRIPTION !
- *
* @author GoodforGod
* @since 29.10.2018
*/
diff --git a/src/main/java/io/api/etherscan/model/utility/BalanceTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/BalanceTO.java
similarity index 80%
rename from src/main/java/io/api/etherscan/model/utility/BalanceTO.java
rename to src/main/java/io/goodforgod/api/etherscan/model/response/BalanceTO.java
index 8d9d9b7..8f56790 100644
--- a/src/main/java/io/api/etherscan/model/utility/BalanceTO.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/response/BalanceTO.java
@@ -1,8 +1,6 @@
-package io.api.etherscan.model.utility;
+package io.goodforgod.api.etherscan.model.response;
/**
- * ! NO DESCRIPTION !
- *
* @author GoodforGod
* @since 29.10.2018
*/
diff --git a/src/main/java/io/api/etherscan/model/utility/BaseListResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/BaseListResponseTO.java
similarity index 78%
rename from src/main/java/io/api/etherscan/model/utility/BaseListResponseTO.java
rename to src/main/java/io/goodforgod/api/etherscan/model/response/BaseListResponseTO.java
index 28f01f3..0d1b06c 100644
--- a/src/main/java/io/api/etherscan/model/utility/BaseListResponseTO.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/response/BaseListResponseTO.java
@@ -1,10 +1,8 @@
-package io.api.etherscan.model.utility;
+package io.goodforgod.api.etherscan.model.response;
import java.util.List;
/**
- * ! NO DESCRIPTION !
- *
* @author GoodforGod
* @since 30.10.2018
*/
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/BaseResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/BaseResponseTO.java
new file mode 100644
index 0000000..46c6ca0
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/response/BaseResponseTO.java
@@ -0,0 +1,23 @@
+package io.goodforgod.api.etherscan.model.response;
+
+import io.goodforgod.api.etherscan.util.BasicUtils;
+
+/**
+ * @author GoodforGod
+ * @since 29.10.2018
+ */
+public abstract class BaseResponseTO {
+
+ String status;
+ String message;
+
+ public int getStatus() {
+ return BasicUtils.isEmpty(status)
+ ? -1
+ : Integer.parseInt(status);
+ }
+
+ public String getMessage() {
+ return message;
+ }
+}
diff --git a/src/main/java/io/api/etherscan/model/utility/BlockParam.java b/src/main/java/io/goodforgod/api/etherscan/model/response/BlockParam.java
similarity index 86%
rename from src/main/java/io/api/etherscan/model/utility/BlockParam.java
rename to src/main/java/io/goodforgod/api/etherscan/model/response/BlockParam.java
index 0f027ec..b2dbd32 100644
--- a/src/main/java/io/api/etherscan/model/utility/BlockParam.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/response/BlockParam.java
@@ -1,8 +1,6 @@
-package io.api.etherscan.model.utility;
+package io.goodforgod.api.etherscan.model.response;
/**
- * ! NO DESCRIPTION !
- *
* @author GoodforGod
* @since 31.10.2018
*/
diff --git a/src/main/java/io/api/etherscan/model/utility/BlockResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/BlockResponseTO.java
similarity index 54%
rename from src/main/java/io/api/etherscan/model/utility/BlockResponseTO.java
rename to src/main/java/io/goodforgod/api/etherscan/model/response/BlockResponseTO.java
index 0d63184..363612b 100644
--- a/src/main/java/io/api/etherscan/model/utility/BlockResponseTO.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/response/BlockResponseTO.java
@@ -1,10 +1,8 @@
-package io.api.etherscan.model.utility;
+package io.goodforgod.api.etherscan.model.response;
-import io.api.etherscan.model.Block;
+import io.goodforgod.api.etherscan.model.Block;
/**
- * ! NO DESCRIPTION !
- *
* @author GoodforGod
* @since 30.10.2018
*/
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/EthSupplyResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/EthSupplyResponseTO.java
new file mode 100644
index 0000000..edbc2e3
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/response/EthSupplyResponseTO.java
@@ -0,0 +1,16 @@
+package io.goodforgod.api.etherscan.model.response;
+
+import io.goodforgod.api.etherscan.model.EthSupply;
+
+/**
+ * @author GoodforGod
+ * @since 14.05.2023
+ */
+public class EthSupplyResponseTO extends BaseResponseTO {
+
+ private EthSupply result;
+
+ public EthSupply getResult() {
+ return result;
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/GasEstimateResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/GasEstimateResponseTO.java
new file mode 100644
index 0000000..96432f3
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/response/GasEstimateResponseTO.java
@@ -0,0 +1,14 @@
+package io.goodforgod.api.etherscan.model.response;
+
+/**
+ * @author Abhay Gupta
+ * @since 14.11.2022
+ */
+public class GasEstimateResponseTO extends BaseResponseTO {
+
+ private String result;
+
+ public String getResult() {
+ return result;
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/GasOracleResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/GasOracleResponseTO.java
new file mode 100644
index 0000000..751854c
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/response/GasOracleResponseTO.java
@@ -0,0 +1,16 @@
+package io.goodforgod.api.etherscan.model.response;
+
+import io.goodforgod.api.etherscan.model.GasOracle;
+
+/**
+ * @author Abhay Gupta
+ * @since 14.11.2022
+ */
+public class GasOracleResponseTO extends BaseResponseTO {
+
+ private GasOracle result;
+
+ public GasOracle getResult() {
+ return result;
+ }
+}
diff --git a/src/main/java/io/api/etherscan/model/utility/LogResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/LogResponseTO.java
similarity index 53%
rename from src/main/java/io/api/etherscan/model/utility/LogResponseTO.java
rename to src/main/java/io/goodforgod/api/etherscan/model/response/LogResponseTO.java
index bba1c24..748d155 100644
--- a/src/main/java/io/api/etherscan/model/utility/LogResponseTO.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/response/LogResponseTO.java
@@ -1,10 +1,8 @@
-package io.api.etherscan.model.utility;
+package io.goodforgod.api.etherscan.model.response;
-import io.api.etherscan.model.Log;
+import io.goodforgod.api.etherscan.model.Log;
/**
- * ! NO DESCRIPTION !
- *
* @author GoodforGod
* @since 31.10.2018
*/
diff --git a/src/main/java/io/api/etherscan/model/utility/PriceResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/PriceResponseTO.java
similarity index 65%
rename from src/main/java/io/api/etherscan/model/utility/PriceResponseTO.java
rename to src/main/java/io/goodforgod/api/etherscan/model/response/PriceResponseTO.java
index 3179a73..e2f0b63 100644
--- a/src/main/java/io/api/etherscan/model/utility/PriceResponseTO.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/response/PriceResponseTO.java
@@ -1,10 +1,8 @@
-package io.api.etherscan.model.utility;
+package io.goodforgod.api.etherscan.model.response;
-import io.api.etherscan.model.Price;
+import io.goodforgod.api.etherscan.model.Price;
/**
- * ! NO DESCRIPTION !
- *
* @author GoodforGod
* @since 30.10.2018
*/
diff --git a/src/main/java/io/api/etherscan/model/utility/ReceiptStatusResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/ReceiptStatusResponseTO.java
similarity index 77%
rename from src/main/java/io/api/etherscan/model/utility/ReceiptStatusResponseTO.java
rename to src/main/java/io/goodforgod/api/etherscan/model/response/ReceiptStatusResponseTO.java
index 87e3950..922c5e2 100644
--- a/src/main/java/io/api/etherscan/model/utility/ReceiptStatusResponseTO.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/response/ReceiptStatusResponseTO.java
@@ -1,8 +1,6 @@
-package io.api.etherscan.model.utility;
+package io.goodforgod.api.etherscan.model.response;
/**
- * ! NO DESCRIPTION !
- *
* @author GoodforGod
* @since 03.11.2018
*/
diff --git a/src/main/java/io/api/etherscan/model/utility/ReceiptStatusTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/ReceiptStatusTO.java
similarity index 72%
rename from src/main/java/io/api/etherscan/model/utility/ReceiptStatusTO.java
rename to src/main/java/io/goodforgod/api/etherscan/model/response/ReceiptStatusTO.java
index 6b7995d..4f4717c 100644
--- a/src/main/java/io/api/etherscan/model/utility/ReceiptStatusTO.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/response/ReceiptStatusTO.java
@@ -1,8 +1,6 @@
-package io.api.etherscan.model.utility;
+package io.goodforgod.api.etherscan.model.response;
/**
- * ! NO DESCRIPTION !
- *
* @author GoodforGod
* @since 03.11.2018
*/
diff --git a/src/main/java/io/api/etherscan/model/utility/StatusResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/StatusResponseTO.java
similarity index 66%
rename from src/main/java/io/api/etherscan/model/utility/StatusResponseTO.java
rename to src/main/java/io/goodforgod/api/etherscan/model/response/StatusResponseTO.java
index bc10eb7..6847930 100644
--- a/src/main/java/io/api/etherscan/model/utility/StatusResponseTO.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/response/StatusResponseTO.java
@@ -1,10 +1,8 @@
-package io.api.etherscan.model.utility;
+package io.goodforgod.api.etherscan.model.response;
-import io.api.etherscan.model.Status;
+import io.goodforgod.api.etherscan.model.Status;
/**
- * ! NO DESCRIPTION !
- *
* @author GoodforGod
* @since 30.10.2018
*/
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/StringResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/StringResponseTO.java
new file mode 100644
index 0000000..19fa0a1
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/response/StringResponseTO.java
@@ -0,0 +1,50 @@
+package io.goodforgod.api.etherscan.model.response;
+
+/**
+ * @author GoodforGod
+ * @since 29.10.2018
+ */
+public class StringResponseTO extends BaseResponseTO {
+
+ private String result;
+
+ public String getResult() {
+ return result;
+ }
+
+ public static StringResponseBuilder builder() {
+ return new StringResponseBuilder();
+ }
+
+ public static final class StringResponseBuilder {
+
+ private String status;
+ private String message;
+ private String result;
+
+ private StringResponseBuilder() {}
+
+ public StringResponseBuilder withStatus(String status) {
+ this.status = status;
+ return this;
+ }
+
+ public StringResponseBuilder withMessage(String message) {
+ this.message = message;
+ return this;
+ }
+
+ public StringResponseBuilder withResult(String result) {
+ this.result = result;
+ return this;
+ }
+
+ public StringResponseTO build() {
+ StringResponseTO stringResponseTO = new StringResponseTO();
+ stringResponseTO.status = this.status;
+ stringResponseTO.message = this.message;
+ stringResponseTO.result = this.result;
+ return stringResponseTO;
+ }
+ }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc1155ResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc1155ResponseTO.java
new file mode 100644
index 0000000..3bf9d49
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc1155ResponseTO.java
@@ -0,0 +1,11 @@
+package io.goodforgod.api.etherscan.model.response;
+
+import io.goodforgod.api.etherscan.model.TxErc1155;
+
+/**
+ * @author GoodforGod
+ * @since 14.05.2023
+ */
+public class TxErc1155ResponseTO extends BaseListResponseTO {
+
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc20ResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc20ResponseTO.java
new file mode 100644
index 0000000..d5d3f6e
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc20ResponseTO.java
@@ -0,0 +1,11 @@
+package io.goodforgod.api.etherscan.model.response;
+
+import io.goodforgod.api.etherscan.model.TxErc20;
+
+/**
+ * @author GoodforGod
+ * @since 29.10.2018
+ */
+public class TxErc20ResponseTO extends BaseListResponseTO {
+
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc721ResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc721ResponseTO.java
new file mode 100644
index 0000000..27518ae
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc721ResponseTO.java
@@ -0,0 +1,11 @@
+package io.goodforgod.api.etherscan.model.response;
+
+import io.goodforgod.api.etherscan.model.TxErc721;
+
+/**
+ * @author GoodforGod
+ * @since 14.05.2023
+ */
+public class TxErc721ResponseTO extends BaseListResponseTO {
+
+}
diff --git a/src/main/java/io/api/etherscan/model/utility/TxInternalResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/TxInternalResponseTO.java
similarity index 55%
rename from src/main/java/io/api/etherscan/model/utility/TxInternalResponseTO.java
rename to src/main/java/io/goodforgod/api/etherscan/model/response/TxInternalResponseTO.java
index d38a879..efcf4dd 100644
--- a/src/main/java/io/api/etherscan/model/utility/TxInternalResponseTO.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/response/TxInternalResponseTO.java
@@ -1,10 +1,8 @@
-package io.api.etherscan.model.utility;
+package io.goodforgod.api.etherscan.model.response;
-import io.api.etherscan.model.TxInternal;
+import io.goodforgod.api.etherscan.model.TxInternal;
/**
- * ! NO DESCRIPTION !
- *
* @author GoodforGod
* @since 29.10.2018
*/
diff --git a/src/main/java/io/api/etherscan/model/utility/TxResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/TxResponseTO.java
similarity index 53%
rename from src/main/java/io/api/etherscan/model/utility/TxResponseTO.java
rename to src/main/java/io/goodforgod/api/etherscan/model/response/TxResponseTO.java
index 53cce38..f4bd3f0 100644
--- a/src/main/java/io/api/etherscan/model/utility/TxResponseTO.java
+++ b/src/main/java/io/goodforgod/api/etherscan/model/response/TxResponseTO.java
@@ -1,10 +1,8 @@
-package io.api.etherscan.model.utility;
+package io.goodforgod.api.etherscan.model.response;
-import io.api.etherscan.model.Tx;
+import io.goodforgod.api.etherscan.model.Tx;
/**
- * ! NO DESCRIPTION !
- *
* @author GoodforGod
* @since 29.10.2018
*/
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/UncleBlockResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/UncleBlockResponseTO.java
new file mode 100644
index 0000000..de94f9e
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/response/UncleBlockResponseTO.java
@@ -0,0 +1,16 @@
+package io.goodforgod.api.etherscan.model.response;
+
+import io.goodforgod.api.etherscan.model.BlockUncle;
+
+/**
+ * @author GoodforGod
+ * @since 30.10.2018
+ */
+public class UncleBlockResponseTO extends BaseResponseTO {
+
+ private BlockUncle result;
+
+ public BlockUncle getResult() {
+ return result;
+ }
+}
diff --git a/src/main/java/io/api/etherscan/util/BasicUtils.java b/src/main/java/io/goodforgod/api/etherscan/util/BasicUtils.java
similarity index 74%
rename from src/main/java/io/api/etherscan/util/BasicUtils.java
rename to src/main/java/io/goodforgod/api/etherscan/util/BasicUtils.java
index 96b855d..216ab62 100644
--- a/src/main/java/io/api/etherscan/util/BasicUtils.java
+++ b/src/main/java/io/goodforgod/api/etherscan/util/BasicUtils.java
@@ -1,15 +1,15 @@
-package io.api.etherscan.util;
-
-import io.api.etherscan.error.EtherScanException;
-import io.api.etherscan.error.InvalidAddressException;
-import io.api.etherscan.error.InvalidTxHashException;
-import io.api.etherscan.model.utility.BaseResponseTO;
-import io.api.etherscan.model.utility.BlockParam;
-import org.jetbrains.annotations.NotNull;
-
+package io.goodforgod.api.etherscan.util;
+
+import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException;
+import io.goodforgod.api.etherscan.error.EtherScanInvalidTxHashException;
+import io.goodforgod.api.etherscan.error.EtherScanResponseException;
+import io.goodforgod.api.etherscan.model.response.BaseResponseTO;
+import io.goodforgod.api.etherscan.model.response.BlockParam;
+import io.goodforgod.api.etherscan.model.response.StringResponseTO;
import java.math.BigInteger;
import java.util.*;
import java.util.regex.Pattern;
+import org.jetbrains.annotations.NotNull;
/**
* Basic utils for library
@@ -17,17 +17,17 @@
* @author GoodforGod
* @since 28.10.2018
*/
-public class BasicUtils {
+public final class BasicUtils {
- private static final int MAX_END_BLOCK = 999999999;
+ private BasicUtils() {}
+
+ private static final int MAX_END_BLOCK = Integer.MAX_VALUE;
private static final int MIN_START_BLOCK = 0;
private static final Pattern ADDRESS_PATTERN = Pattern.compile("0x[a-zA-Z0-9]{40}");
private static final Pattern TXHASH_PATTERN = Pattern.compile("0x[a-zA-Z0-9]{64}");
private static final Pattern HEX_PATTERN = Pattern.compile("[a-zA-Z0-9]+");
- private BasicUtils() {}
-
public static boolean isEmpty(String value) {
return value == null || value.isEmpty();
}
@@ -79,7 +79,7 @@ public static BigInteger parseHex(String hex) {
return BigInteger.valueOf(0);
final String formatted = (hex.length() > 2 && hex.charAt(0) == '0' && hex.charAt(1) == 'x')
- ? hex.substring(2, hex.length())
+ ? hex.substring(2)
: hex;
return new BigInteger(formatted, 16);
@@ -90,24 +90,29 @@ public static BigInteger parseHex(String hex) {
public static void validateAddress(String address) {
if (isNotAddress(address))
- throw new InvalidAddressException("Address [" + address + "] is not Ethereum based.");
+ throw new EtherScanInvalidAddressException("Address [" + address + "] is not Ethereum based.");
}
public static void validateTxHash(String txhash) {
if (isNotTxHash(txhash))
- throw new InvalidTxHashException("TxHash [" + txhash + "] is not Ethereum based.");
+ throw new EtherScanInvalidTxHashException("TxHash [" + txhash + "] is not Ethereum based.");
}
public static void validateTxResponse(T response) {
- if (response == null)
- throw new EtherScanException("EtherScan responded with null value");
+ if (response == null) {
+ final StringResponseTO emptyResponse = StringResponseTO.builder()
+ .withStatus("0")
+ .withMessage("EtherScan responded with null value")
+ .build();
+ throw new EtherScanResponseException(emptyResponse, "EtherScan responded with null value");
+ }
if (response.getStatus() != 1) {
if (response.getMessage() == null) {
- throw new EtherScanException(
+ throw new EtherScanResponseException(response,
"Unexpected Etherscan exception, no information from server about error, code " + response.getStatus());
} else if (!response.getMessage().startsWith("No tra") && !response.getMessage().startsWith("No rec")) {
- throw new EtherScanException(response);
+ throw new EtherScanResponseException(response);
}
}
}
@@ -115,7 +120,7 @@ public static void validateTxResponse(T response) {
public static void validateAddresses(List