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/master.yml
similarity index 50%
rename from .github/workflows/gradle.yml
rename to .github/workflows/master.yml
index 613a39e..32d0d9f 100644
--- a/.github/workflows/gradle.yml
+++ b/.github/workflows/master.yml
@@ -1,13 +1,11 @@
-name: Java CI
+name: CI Master
 
 on:
   push:
     branches:
       - master
-  pull_request:
-    branches:
-      - master
-      - dev
+  schedule:
+    - cron: 0 0 * * 0
 
 jobs:
   build:
@@ -15,29 +13,27 @@ jobs:
     strategy:
       matrix:
         java: [ '11' ]
-    name: Java ${{ matrix.java }} setup
+    name: Master Java ${{ matrix.java }} action
 
     steps:
-      - uses: actions/checkout@v1
+      - uses: actions/checkout@v3
       - name: Set up JDK
-        uses: actions/setup-java@v1
-
+        uses: actions/setup-java@v3
         with:
           java-version: ${{ matrix.java }}
+          distribution: 'adopt'
 
       - name: Build
-        run: ./gradlew classes
-
-      - name: Codestyle
-        run: ./gradlew spotlessCheck
+        run: './gradlew classes'
 
       - name: Test
-        run: ./gradlew test jacocoTestReport
+        run: './gradlew test jacocoTestReport'
         env:
-          API_KEY: ${{ secrets.API_KEY }}
+          ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY }}
 
       - name: SonarQube
-        run: ./gradlew sonarqube
+        if: matrix.java == '11'
+        run: './gradlew sonar --info'
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
           SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml
new file mode 100644
index 0000000..ed10cc4
--- /dev/null
+++ b/.github/workflows/publish-release.yml
@@ -0,0 +1,49 @@
+name: Release
+
+on:
+  release:
+    types: [ published ]
+
+jobs:
+  publish-release:
+    runs-on: ubuntu-latest
+    name: Publish Release
+
+    steps:
+      - uses: actions/checkout@v3
+      - name: Set up JDK
+        uses: actions/setup-java@v3
+        with:
+          java-version: '17'
+          distribution: 'adopt'
+
+      - name: Build
+        run: './gradlew classes'
+
+      - name: Test
+        run: './gradlew test jacocoTestReport'
+        env:
+          ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY_PUBLISH }}
+
+      - name: SonarQube
+        run: './gradlew sonar --info'
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+
+      - name: Publish Release to GitHub Packages
+        run: './gradlew publishMavenJavaPublicationToGitHubPackagesRepository'
+        env:
+          RELEASE_VERSION: ${{ github.ref_name }}
+          GITHUB_TOKEN: ${{ secrets.OSS_GITHUB_TOKEN }}
+          ORG_GRADLE_PROJECT_signingKey: ${{ secrets.OSS_SIGNING_KEY }}
+          ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.OSS_SIGNING_PASSWORD }}
+
+      - name: Publish Release to OSSRH
+        run: './gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository'
+        env:
+          RELEASE_VERSION: ${{ github.ref_name }}
+          OSS_USERNAME: ${{ secrets.OSS_USERNAME }}
+          OSS_PASSWORD: ${{ secrets.OSS_PASSWORD }}
+          ORG_GRADLE_PROJECT_signingKey: ${{ secrets.OSS_SIGNING_KEY }}
+          ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.OSS_SIGNING_PASSWORD }}
diff --git a/.github/workflows/publish-snapshot.yml b/.github/workflows/publish-snapshot.yml
new file mode 100644
index 0000000..4a4d958
--- /dev/null
+++ b/.github/workflows/publish-snapshot.yml
@@ -0,0 +1,44 @@
+name: Snapshot
+
+on:
+  push:
+    paths:
+      - '**/workflows/*.yml'
+      - '**/java/**'
+      - '*.java'
+      - '*.gradle'
+      - '*.properties'
+    branches:
+      - dev
+
+jobs:
+  publish-snapshot:
+    runs-on: ubuntu-latest
+    name: Publish Snapshot
+
+    steps:
+      - uses: actions/checkout@v3
+      - name: Set up JDK
+        uses: actions/setup-java@v3
+        with:
+          java-version: '17'
+          distribution: 'adopt'
+
+      - name: Code Style
+        run: './gradlew spotlessCheck'
+
+      - name: Build
+        run: './gradlew classes'
+
+      - name: Test
+        run: './gradlew test jacocoTestReport'
+        env:
+          ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY_PUBLISH }}
+
+      - name: Publish Snapshot
+        run: './gradlew publish'
+        env:
+          OSS_USERNAME: ${{ secrets.OSS_USERNAME }}
+          OSS_PASSWORD: ${{ secrets.OSS_PASSWORD }}
+          ORG_GRADLE_PROJECT_signingKey: ${{ secrets.OSS_SIGNING_KEY }}
+          ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.OSS_SIGNING_PASSWORD }}
diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml
new file mode 100644
index 0000000..c9c6a99
--- /dev/null
+++ b/.github/workflows/pull-request.yml
@@ -0,0 +1,41 @@
+name: CI Pull Request
+
+on:
+  pull_request:
+    branches:
+      - master
+      - dev
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        java: [ '11' ]
+    name: Pull Request Java ${{ matrix.java }} action
+
+    steps:
+      - uses: actions/checkout@v3
+      - name: Set up JDK
+        uses: actions/setup-java@v3
+        with:
+          java-version: ${{ matrix.java }}
+          distribution: 'adopt'
+
+      - name: Code Style
+        run: './gradlew spotlessCheck'
+
+      - name: Build
+        run: './gradlew classes'
+
+      - name: Test
+        run: './gradlew test jacocoTestReport'
+        env:
+          ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY }}
+
+      - name: SonarQube
+        if: matrix.java == '11'
+        run: './gradlew sonar --info'
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
diff --git a/.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/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..5abd8dc
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,23 @@
+# Contributing Code or Documentation Guide
+
+## Running Tests
+
+The new code should contain tests that check new behavior.
+
+Run tests `./gradlew test` to check that code works as behavior.
+
+## Code Style
+
+The code base should remain clean, following industry best practices for organization, javadoc and style, as much as possible.
+
+To run the Code Style check use `./gradlew spotlessCheck`.
+
+If check found any errors, you can apply Code Style by running `./gradlew spotlessApply`
+
+## Creating a pull request
+
+Once you are satisfied with your changes:
+
+- Commit changes to the local branch you created.
+- Push that branch with changes to the corresponding remote branch on GitHub
+- Submit a [pull request](https://help.github.com/articles/creating-a-pull-request) to `dev` branch.
\ No newline at end of file
diff --git a/README.md b/README.md
index 4468a8d..c09c885 100644
--- a/README.md
+++ b/README.md
@@ -1,22 +1,21 @@
 # Java EtherScan API 
 
-[![GitHub Action](https://github.com/goodforgod/java-etherscan-api/workflows/Java%20CI/badge.svg)](https://github.com/GoodforGod/java-etherscan-api/actions?query=workflow%3A%22Java+CI%22)
+[![Minimum required Java version](https://img.shields.io/badge/Java-11%2B-blue?logo=openjdk)](https://openjdk.org/projects/jdk/11/)
+[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.goodforgod/java-etherscan-api/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.goodforgod/java-etherscan-api)
+[![Java CI](https://github.com/GoodforGod/java-etherscan-api/workflows/CI%20Master/badge.svg)](https://github.com/GoodforGod/java-etherscan-api/actions?query=workflow%3ACI+Master)
 [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=GoodforGod_java-etherscan-api&metric=coverage)](https://sonarcloud.io/dashboard?id=GoodforGod_java-etherscan-api)
 [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=GoodforGod_java-etherscan-api&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=GoodforGod_java-etherscan-api)
 [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=GoodforGod_java-etherscan-api&metric=ncloc)](https://sonarcloud.io/dashboard?id=GoodforGod_java-etherscan-api)
-[![](https://jitpack.io/v/GoodforGod/java-etherscan-api.svg)](https://jitpack.io/#GoodforGod/java-etherscan-api)
 
-[Etherscan.io](https://etherscan.io/apis) Java API implementation.
+[Etherscan.io](https://docs.etherscan.io/) Java API implementation.
 
-Library supports all available EtherScan *API* calls for all available *Ethereum Networks* for *etherscan.io*
+Library supports EtherScan *API* for all available *Ethereum Networks* for *etherscan.io*
 
 ## Dependency :rocket:
 
 **Gradle**
 ```groovy
-dependencies {
-    compile "com.github.goodforgod:java-etherscan-api:1.2.0"
-}
+implementation "com.github.goodforgod:java-etherscan-api:3.0.0"
 ```
 
 **Maven**
@@ -24,7 +23,7 @@ dependencies {
 <dependency>
     <groupId>com.github.goodforgod</groupId>
     <artifactId>java-etherscan-api</artifactId>
-    <version>1.2.0</version>
+    <version>3.0.0</version>
 </dependency>
 ```
 
@@ -42,143 +41,150 @@ 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.builder().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<IHttpExecutor> supplier = () -> new HttpExecutor(connectionTimeout);
-Supplier<IHttpExecutor> supplierFull = () -> new HttpExecutor(connectionTimeout, readTimeout);
- 
-EtherScanApi api = new EtherScanApi(EthNetwork.RINKEBY, supplier);
-EtherScanApi apiWithKey = new EtherScanApi("YourApiKey", EthNetwork.MAINNET, supplierFull);
+Supplier<EthHttpClient> ethHttpClientSupplier = () -> new UrlEthHttpClient(Duration.ofMillis(300), Duration.ofMillis(300));
+EtherScanAPI api = EtherScanAPI.builder()
+    .withHttpClient(supplier)
+    .build();
+```
+
+Also you can use Java 11+ HttpClient:
+```java
+Supplier<EthHttpClient> ethHttpClientSupplier = () -> new JdkEthHttpClient();
+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.builder().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.builder().build();
 Optional<UncleBlock> 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.builder().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.builder().build();
+LogQuery query = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c")
+           .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545")
            .build();
 List<Log> 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.builder().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<Log> 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);
-Optional<TxProxy> tx = api.proxy().tx("0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1");
+EtherScanAPI api = EtherScanAPI.builder().build();
+Optional<TxProxy> tx = api.proxy().tx("0x1e2910a263.0.08d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1");
 ```
 
 **Get block info with proxy endpoint**
-
 ```java
-EtherScanApi api = new EtherScanApi(EthNetwork.MAINNET);
+EtherScanAPI api = EtherScanAPI.builder().build();
 Optional<BlockProxy> 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.builder().build();
+Price price = api.stats().priceLast();
 ```
 
-### Transaction Api
+### Transaction API
 
 **Request receipt status for tx**
-
 ```java
-EtherScanApi api = new EtherScanApi();
+EtherScanAPI api = EtherScanAPI.builder().build();
 Optional<Boolean> 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/_config.yml b/_config.yml
deleted file mode 100644
index 2f7efbe..0000000
--- a/_config.yml
+++ /dev/null
@@ -1 +0,0 @@
-theme: jekyll-theme-minimal
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index f599905..b9be8cd 100644
--- a/build.gradle
+++ b/build.gradle
@@ -3,8 +3,9 @@ plugins {
     id "java-library"
     id "maven-publish"
 
-    id "org.sonarqube" version "3.3"
-    id "com.diffplug.spotless" version "5.14.3"
+    id "org.sonarqube" version "6.3.1.5724"
+    id "com.diffplug.spotless" version "6.19.0"
+    id "io.github.gradle-nexus.publish-plugin" version "2.0.0"
 }
 
 repositories {
@@ -13,16 +14,46 @@ repositories {
 }
 
 group = groupId
-version = artifactVersion
+var ver = System.getenv().getOrDefault("RELEASE_VERSION", artifactVersion)
+version = ver.startsWith("v") ? ver.substring(1) : ver
 
-sourceCompatibility = JavaVersion.VERSION_1_8
-targetCompatibility = JavaVersion.VERSION_1_8
+sourceCompatibility = JavaVersion.VERSION_11
+targetCompatibility = JavaVersion.VERSION_11
+
+dependencies {
+    compileOnly "org.jetbrains:annotations:24.0.1"
+    implementation "io.goodforgod:gson-configuration:2.0.0"
+
+    testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.11.4"
+    testImplementation "org.junit.jupiter:junit-jupiter-api:5.11.4"
+    testImplementation "org.junit.jupiter:junit-jupiter-params:5.11.4"
+}
+
+test {
+    failFast(false)
+    useJUnitPlatform()
+    testLogging {
+        events("passed", "skipped", "failed")
+        exceptionFormat("full")
+        showStandardStreams(false)
+    }
+
+    reports {
+        html.required = false
+        junitXml.required = true
+    }
+
+    environment([
+            "": "",
+    ])
+}
 
 spotless {
     java {
-        encoding "UTF-8"
+        encoding("UTF-8")
+        importOrder()
         removeUnusedImports()
-        eclipse().configFile "${projectDir}/config/codestyle.xml"
+        eclipse("4.21").configFile("${rootDir}/config/codestyle.xml")
     }
 }
 
@@ -30,27 +61,19 @@ sonarqube {
     properties {
         property "sonar.host.url", "https://sonarcloud.io"
         property "sonar.organization", "goodforgod"
-        property "sonar.projectKey", "GoodforGod_java-etherscan-api"
+        property "sonar.projectKey", "GoodforGod_$artifactId"
     }
 }
 
-dependencies {
-    implementation "org.jetbrains:annotations:22.0.0"
-    implementation "com.google.code.gson:gson:2.8.9"
-
-    testImplementation "junit:junit:4.13.1"
-}
-
-test {
-    useJUnit()
-    testLogging {
-        events("passed", "skipped", "failed")
-        exceptionFormat("full")
-    }
-
-    reports {
-        html.enabled(false)
-        junitXml.enabled(false)
+nexusPublishing {
+    packageGroup = groupId
+    repositories {
+        sonatype {
+            username = System.getenv("OSS_USERNAME")
+            password = System.getenv("OSS_PASSWORD")
+            nexusUrl.set(uri("https://ossrh-staging-api.central.sonatype.com/service/local/"))
+            snapshotRepositoryUrl.set(uri("https://central.sonatype.com/repository/maven-snapshots/"))
+        }
     }
 }
 
@@ -61,12 +84,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,23 +101,33 @@ 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"
                 }
             }
         }
     }
     repositories {
         maven {
-            def releasesRepoUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2"
-            def snapshotsRepoUrl = "https://oss.sonatype.org/content/repositories/snapshots/"
+            def releasesRepoUrl = "https://ossrh-staging-api.central.sonatype.com/service/local/"
+            def snapshotsRepoUrl = "https://central.sonatype.com/repository/maven-snapshots/"
             url = version.endsWith("SNAPSHOT") ? snapshotsRepoUrl : releasesRepoUrl
             credentials {
                 username System.getenv("OSS_USERNAME")
                 password System.getenv("OSS_PASSWORD")
             }
         }
+        if (!version.endsWith("SNAPSHOT")) {
+            maven {
+                name = "GitHubPackages"
+                url = "https://maven.pkg.github.com/GoodforGod/$artifactId"
+                credentials {
+                    username = System.getenv("GITHUB_ACTOR")
+                    password = System.getenv("GITHUB_TOKEN")
+                }
+            }
+        }
     }
 }
 
@@ -112,7 +145,7 @@ tasks.withType(JavaCompile) {
 check.dependsOn jacocoTestReport
 jacocoTestReport {
     reports {
-        xml.enabled true
+        xml.required = true
         html.destination file("${buildDir}/jacocoHtml")
     }
 }
@@ -124,9 +157,12 @@ javadoc {
     }
 }
 
-if (project.hasProperty("signing.keyId")) {
+if (project.hasProperty("signingKey")) {
     apply plugin: "signing"
     signing {
+        def signingKey = findProperty("signingKey")
+        def signingPassword = findProperty("signingPassword")
+        useInMemoryPgpKeys(signingKey, signingPassword)
         sign publishing.publications.mavenJava
     }
 }
diff --git a/config/codestyle.xml b/config/codestyle.xml
index a90c4f5..ad0c929 100644
--- a/config/codestyle.xml
+++ b/config/codestyle.xml
@@ -1,156 +1,95 @@
 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<profiles version="16">
-    <profile kind="CodeFormatterProfile" name="Orgstaff" version="16">
+<profiles version="21">
+    <profile kind="CodeFormatterProfile" name="Anton Kurako (GoodforGod)" version="21">
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment" value="common_lines"/>
         <setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_logical_operator" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation" value="common_lines"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement" value="common_lines"/>
         <setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
         <setting id="org.eclipse.jdt.core.formatter.indentation.size" value="1"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration" value="common_lines"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.align_with_spaces" value="false"/>
-        <setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
         <setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="2"/>
-        <setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="49"/>
-        <setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
+        <setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_before_code_block" value="0"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_switch_case_expressions" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement" value="common_lines"/>
         <setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="false"/>
         <setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true"/>
         <setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position" value="true"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="0"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_record_components" value="82"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator" value="true"/>
         <setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references" value="0"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.alignment_for_logical_operator" value="16"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line" value="one_line_never"/>
+        <setting id="org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line" value="one_line_if_empty"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_record_declaration" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
-        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped" value="true"/>
-        <setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="100"/>
-        <setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="false"/>
-        <setting id="org.eclipse.jdt.core.formatter.keep_method_body_on_one_line" value="one_line_if_empty"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line" value="one_line_never"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
-        <setting id="org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line" value="one_line_never"/>
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_before_abstract_method" value="1"/>
+        <setting id="org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line" value="one_line_if_empty"/>
         <setting id="org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns" value="false"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line" value="one_line_never"/>
-        <setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="-1"/>
         <setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
         <setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause" value="common_lines"/>
-        <setting id="org.eclipse.jdt.core.formatter.alignment_for_additive_operator" value="16"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_relational_operator" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator" value="16"/>
         <setting id="org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line" value="one_line_if_single_item"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_switch_case_expressions" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.wrap_before_shift_operator" value="true"/>
         <setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
-        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
-        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_lambda_body" value="end_of_line"/>
-        <setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_code_block" value="-1"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="false"/>
-        <setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="18"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="82"/>
         <setting id="org.eclipse.jdt.core.formatter.alignment_for_type_parameters" value="0"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
-        <setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
         <setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_loops" value="16"/>
         <setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="true"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line" value="false"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
-        <setting id="org.eclipse.jdt.core.formatter.alignment_for_relational_operator" value="0"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
         <setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="true"/>
-        <setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
         <setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation" value="common_lines"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_additive_operator" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_enum_constant" value="49"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.text_block_indentation" value="0"/>
         <setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
         <setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="0"/>
         <setting id="org.eclipse.jdt.core.formatter.alignment_for_module_statements" value="16"/>
@@ -158,189 +97,292 @@
         <setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions" value="false"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
         <setting id="org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line" value="one_line_never"/>
         <setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="1"/>
-        <setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="80"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns" value="false"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
         <setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain" value="0"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain" value="49"/>
         <setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="true"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_type_annotations" value="80"/>
         <setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_additive_operator" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.wrap_before_assertion_message_operator" value="true"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
-        <setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="false"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.wrap_before_conditional_operator" value="true"/>
-        <setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.alignment_for_shift_operator" value="0"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="16"/>
         <setting id="org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines" value="2147483647"/>
         <setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
         <setting id="org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator" value="16"/>
         <setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
-        <setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="true"/>
-        <setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause" value="common_lines"/>
         <setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
         <setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
-        <setting id="org.eclipse.jdt.core.formatter.keep_code_block_on_one_line" value="one_line_if_empty"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
-        <setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
-        <setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="80"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.wrap_before_assignment_operator" value="false"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_not_operator" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration" value="common_lines"/>
         <setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
-        <setting id="org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line" value="one_line_if_single_item"/>
         <setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
         <setting id="org.eclipse.jdt.core.formatter.alignment_for_type_arguments" value="0"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="16"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="80"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_package" value="49"/>
         <setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="true"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_case" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_logical_operator" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_record_header" value="true"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator" value="true"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.wrap_before_relational_operator" value="true"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="true"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.comment.indent_tag_description" value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.comment.indent_tag_description" value="true"/>
         <setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_record_constructor" value="end_of_line"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="18"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.alignment_for_string_concatenation" value="16"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
         <setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line" value="false"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
-        <setting id="org.eclipse.jdt.core.formatter.wrap_before_logical_operator" value="true"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_shift_operator" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration" value="common_lines"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_shift_operator" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line" value="false"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line" value="one_line_never"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_record_components" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
-        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header" value="0"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header" value="16"/>
         <setting id="org.eclipse.jdt.core.formatter.wrap_before_additive_operator" value="true"/>
         <setting id="org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line" value="false"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_record_declaration" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_relational_operator" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_logical_operator" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation" value="common_lines"/>
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="1"/>
+        <setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_record_declaration" value="common_lines"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement" value="common_lines"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_default" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="49"/>
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
+        <setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_method_body" value="-1"/>
+        <setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement" value="common_lines"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_case" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="1"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
+        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped" value="true"/>
+        <setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="100"/>
+        <setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.keep_method_body_on_one_line" value="one_line_if_empty"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line" value="one_line_never"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line" value="one_line_if_empty"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_additive_operator" value="16"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_constructor" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_relational_operator" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.keep_record_declaration_on_one_line" value="one_line_if_empty"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
+        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_lambda_body" value="end_of_line"/>
+        <setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_parameter" value="80"/>
+        <setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="true"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_relational_operator" value="0"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="48"/>
+        <setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_additive_operator" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
+        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_record_declaration" value="end_of_line"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_after_code_block" value="0"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="80"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="49"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_type" value="49"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_local_variable" value="49"/>
+        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_default" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_between_different_tags" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_additive_operator" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_field" value="49"/>
+        <setting id="org.eclipse.jdt.core.formatter.wrap_before_conditional_operator" value="true"/>
+        <setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_shift_operator" value="0"/>
+        <setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="true"/>
+        <setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause" value="common_lines"/>
+        <setting id="org.eclipse.jdt.core.formatter.keep_code_block_on_one_line" value="one_line_if_empty"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_record_components" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="2"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_record_declaration" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
+        <setting id="org.eclipse.jdt.core.formatter.wrap_before_assignment_operator" value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line" value="one_line_if_single_item"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_method" value="49"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.keep_record_constructor_on_one_line" value="one_line_if_empty"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_declaration" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_assertion_message" value="0"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_logical_operator" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_record_declaration" value="16"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.wrap_before_relational_operator" value="true"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="true"/>
+        <setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="82"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_after_last_class_body_declaration" value="-1"/>
+        <setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
+        <setting id="org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line" value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.wrap_before_logical_operator" value="true"/>
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_between_statement_group_in_switch" value="0"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration" value="common_lines"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line" value="one_line_if_empty"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
+        <setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="-1"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
+        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="true"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
         <setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="false"/>
+        <setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_code_block" value="-1"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="space"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_after_relational_operator" value="insert"/>
-        <setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.wrap_before_string_concatenation" value="true"/>
-        <setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
         <setting id="org.eclipse.jdt.core.formatter.lineSplit" value="130"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
         <setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
diff --git a/gradle.properties b/gradle.properties
index 4022082..7b97567 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,7 +1,6 @@
 groupId=com.github.goodforgod
 artifactId=java-etherscan-api
-artifactVersion=1.2.0
-buildNumber=1
+artifactVersion=3.0.0-SNAPSHOT
 
 
 ##### GRADLE #####
@@ -9,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
+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.jar b/gradle/wrapper/gradle-wrapper.jar
index 7454180..1b33c55 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index ffed3a2..d4081da 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,7 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index 744e882..23d15a9 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.
@@ -15,81 +15,115 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
+# SPDX-License-Identifier: Apache-2.0
+#
 
 ##############################################################################
-##
-##  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/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+#       within the Gradle project.
+#
+#       You can find Gradle at https://github.com/gradle/gradle/.
+#
 ##############################################################################
 
 # 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_NAME="Gradle"
-APP_BASE_NAME=`basename "$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"'
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
 
 # Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
+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
+CLASSPATH="\\\"\\\""
 
 
 # Determine the Java command to use to start the JVM.
 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,88 +132,120 @@ Please set the JAVA_HOME variable in your environment to match the
 location of your Java installation."
     fi
 else
-    JAVACMD="java"
-    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+    JAVACMD=java
+    if ! command -v java >/dev/null 2>&1
+    then
+        die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
 
 Please set the JAVA_HOME variable in your environment to match the
 location of your Java installation."
+    fi
 fi
 
 # Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "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*)
+        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+        # shellcheck disable=SC2039,SC3045
+        MAX_FD=$( ulimit -H -n ) ||
+            warn "Could not query maximum file descriptor limit"
+    esac
+    case $MAX_FD in  #(
+      '' | soft) :;; #(
+      *)
+        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+        # shellcheck disable=SC2039,SC3045
+        ulimit -n "$MAX_FD" ||
+            warn "Could not set maximum file descriptor limit to $MAX_FD"
+    esac
 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, 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"
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+#     and any embedded shellness will be escaped.
+#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+#     treated as '${Hostname}' itself on the command line.
+
+set -- \
+        "-Dorg.gradle.appname=$APP_BASE_NAME" \
+        -classpath "$CLASSPATH" \
+        -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
+        "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+    die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# 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.
+#
+
+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/gradlew.bat b/gradlew.bat
index 107acd3..db3a6ac 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -13,8 +13,10 @@
 @rem See the License for the specific language governing permissions and
 @rem limitations under the License.
 @rem
+@rem SPDX-License-Identifier: Apache-2.0
+@rem
 
-@if "%DEBUG%" == "" @echo off
+@if "%DEBUG%"=="" @echo off
 @rem ##########################################################################
 @rem
 @rem  Gradle startup script for Windows
@@ -25,7 +27,8 @@
 if "%OS%"=="Windows_NT" setlocal
 
 set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
 set APP_BASE_NAME=%~n0
 set APP_HOME=%DIRNAME%
 
@@ -40,13 +43,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome
 
 set JAVA_EXE=java.exe
 %JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto execute
+if %ERRORLEVEL% equ 0 goto execute
 
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
 
 goto fail
 
@@ -56,32 +59,34 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
 
 if exist "%JAVA_EXE%" goto execute
 
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
 
 goto fail
 
 :execute
 @rem Setup the command line
 
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+set CLASSPATH=
 
 
 @rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
 
 :end
 @rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
+if %ERRORLEVEL% equ 0 goto mainEnd
 
 :fail
 rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
 rem the _cmd.exe /c_ return code!
-if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
 
 :mainEnd
 if "%OS%"=="Windows_NT" endlocal
diff --git a/src/main/java/io/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<Balance> balances(List<String> 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<Tx> txs(String address, long startBlock, long endBlock) throws ApiException;
-
-    @NotNull
-    List<Tx> txs(String address, long startBlock) throws ApiException;
-
-    @NotNull
-    List<Tx> 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<TxInternal> txsInternal(String address, long startBlock, long endBlock) throws ApiException;
-
-    @NotNull
-    List<TxInternal> txsInternal(String address, long startBlock) throws ApiException;
-
-    @NotNull
-    List<TxInternal> 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<TxInternal> 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<TxToken> txsToken(String address, long startBlock, long endBlock) throws ApiException;
-
-    @NotNull
-    List<TxToken> txsToken(String address, long startBlock) throws ApiException;
-
-    @NotNull
-    List<TxToken> 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<TxToken> txsNftToken(String address, long startBlock, long endBlock) throws ApiException;
-
-    @NotNull
-    List<TxToken> txsNftToken(String address, long startBlock) throws ApiException;
-
-    @NotNull
-    List<TxToken> 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<Block> 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<UncleBlock> 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<Log> 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<Balance> balances(final List<String> addresses) throws ApiException {
-        if (BasicUtils.isEmpty(addresses))
-            return Collections.emptyList();
-
-        BasicUtils.validateAddresses(addresses);
-
-        // Maximum addresses in batch request - 20
-        final List<Balance> balances = new ArrayList<>();
-        final List<List<String>> addressesAsBatches = BasicUtils.partition(addresses, 20);
-
-        for (final List<String> 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<String> addresses) {
-        return addresses.stream().collect(Collectors.joining(","));
-    }
-
-    @NotNull
-    @Override
-    public List<Tx> txs(final String address) throws ApiException {
-        return txs(address, MIN_START_BLOCK);
-    }
-
-    @NotNull
-    @Override
-    public List<Tx> txs(final String address, final long startBlock) throws ApiException {
-        return txs(address, startBlock, MAX_END_BLOCK);
-    }
-
-    @NotNull
-    @Override
-    public List<Tx> 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 <T>       responseTO list T type
-     * @param <R>       responseListTO type
-     * @return List of T values
-     */
-    private <T, R extends BaseListResponseTO> List<T> getRequestUsingOffset(final String urlParams,
-                                                                            Class<R> tClass) throws ApiException {
-        final List<T> 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<TxInternal> txsInternal(final String address) throws ApiException {
-        return txsInternal(address, MIN_START_BLOCK);
-    }
-
-    @NotNull
-    @Override
-    public List<TxInternal> txsInternal(final String address, final long startBlock) throws ApiException {
-        return txsInternal(address, startBlock, MAX_END_BLOCK);
-    }
-
-    @NotNull
-    @Override
-    public List<TxInternal> 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<TxInternal> 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<TxToken> txsToken(final String address) throws ApiException {
-        return txsToken(address, MIN_START_BLOCK);
-    }
-
-    @NotNull
-    @Override
-    public List<TxToken> txsToken(final String address, final long startBlock) throws ApiException {
-        return txsToken(address, startBlock, MAX_END_BLOCK);
-    }
-
-    @NotNull
-    @Override
-    public List<TxToken> 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<TxToken> txsNftToken(String address) throws ApiException {
-        return txsNftToken(address, MIN_START_BLOCK);
-    }
-
-    @NotNull
-    @Override
-    public List<TxToken> txsNftToken(String address, long startBlock) throws ApiException {
-        return txsNftToken(address, startBlock, MAX_END_BLOCK);
-    }
-
-    @NotNull
-    @Override
-    public List<TxToken> 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<Block> 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 b89447a..0000000
--- a/src/main/java/io/api/etherscan/core/impl/BasicProvider.java
+++ /dev/null
@@ -1,93 +0,0 @@
-package io.api.etherscan.core.impl;
-
-import com.google.gson.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.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 Gson();
-    }
-
-    <T> T convert(final String json, final Class<T> 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<String, Object> 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> T getRequest(final String urlParameters, final Class<T> tClass) {
-        return convert(getRequest(urlParameters), tClass);
-    }
-
-    <T> T postRequest(final String urlParameters, final String dataToPost, final Class<T> 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<UncleBlock> 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<IHttpExecutor> 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<IHttpExecutor> 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<IHttpExecutor> 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<IHttpExecutor> 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<Log> 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/core/impl/TransactionApiProvider.java b/src/main/java/io/api/etherscan/core/impl/TransactionApiProvider.java
deleted file mode 100644
index 82eb467..0000000
--- a/src/main/java/io/api/etherscan/core/impl/TransactionApiProvider.java
+++ /dev/null
@@ -1,60 +0,0 @@
-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;
-
-import java.util.Optional;
-
-/**
- * Transaction API Implementation
- *
- * @author GoodforGod
- * @see ITransactionApi
- * @since 28.10.2018
- */
-public class TransactionApiProvider extends BasicProvider implements ITransactionApi {
-
-    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);
-    }
-
-    @NotNull
-    @Override
-    public Optional<Status> execStatus(final String txhash) throws ApiException {
-        BasicUtils.validateTxHash(txhash);
-
-        final String urlParams = ACT_EXEC_STATUS_PARAM + TXHASH_PARAM + txhash;
-        final StatusResponseTO response = getRequest(urlParams, StatusResponseTO.class);
-        BasicUtils.validateTxResponse(response);
-
-        return Optional.ofNullable(response.getResult());
-    }
-
-    @NotNull
-    @Override
-    public Optional<Boolean> receiptStatus(final String txhash) throws ApiException {
-        BasicUtils.validateTxHash(txhash);
-
-        final String urlParams = ACT_RECEIPT_STATUS_PARAM + TXHASH_PARAM + txhash;
-        final ReceiptStatusResponseTO response = getRequest(urlParams, ReceiptStatusResponseTO.class);
-        BasicUtils.validateTxResponse(response);
-
-        return (response.getResult() == null || BasicUtils.isEmpty(response.getResult().getStatus()))
-                ? Optional.empty()
-                : Optional.of(response.getResult().getStatus().contains("1"));
-    }
-}
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<String, String> 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<String, String> 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<String, String> 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/Abi.java b/src/main/java/io/api/etherscan/model/Abi.java
deleted file mode 100644
index a48a11d..0000000
--- a/src/main/java/io/api/etherscan/model/Abi.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package io.api.etherscan.model;
-
-import io.api.etherscan.util.BasicUtils;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 31.10.2018
- */
-public class Abi {
-
-    private String contractAbi;
-    private boolean isVerified;
-
-    private Abi(String contractAbi, boolean isVerified) {
-        this.contractAbi = contractAbi;
-        this.isVerified = isVerified;
-    }
-
-    public static Abi verified(String contractAbi) {
-        return new Abi(contractAbi, true);
-    }
-
-    public static Abi nonVerified() {
-        return new Abi("", false);
-    }
-
-    public boolean haveAbi() {
-        return !BasicUtils.isEmpty(contractAbi);
-    }
-
-    public String getContractAbi() {
-        return contractAbi;
-    }
-
-    public boolean isVerified() {
-        return isVerified;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o)
-            return true;
-        if (o == null || getClass() != o.getClass())
-            return false;
-
-        Abi abi = (Abi) o;
-
-        if (isVerified != abi.isVerified)
-            return false;
-        return contractAbi != null ? contractAbi.equals(abi.contractAbi) : abi.contractAbi == null;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = contractAbi != null ? contractAbi.hashCode() : 0;
-        result = 31 * result + (isVerified ? 1 : 0);
-        return result;
-    }
-
-    @Override
-    public String toString() {
-        return "Abi{" +
-                "contractAbi='" + contractAbi + '\'' +
-                ", isVerified=" + isVerified +
-                '}';
-    }
-}
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()));
-    }
-
-    // <editor-fold desc="Getters">
-    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();
-    }
-    // </editor-fold>
-
-    @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 a219e57..0000000
--- a/src/main/java/io/api/etherscan/model/BaseTx.java
+++ /dev/null
@@ -1,122 +0,0 @@
-package io.api.etherscan.model;
-
-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;
-    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;
-
-    // <editor-fold desc="Getter">
-    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;
-    }
-    // </editor-fold>
-
-    @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 d328841..0000000
--- a/src/main/java/io/api/etherscan/model/Block.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package io.api.etherscan.model;
-
-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;
-    private LocalDateTime _timeStamp;
-
-    // <editor-fold desc="Getter">
-    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;
-    }
-    // </editor-fold>
-
-    @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 36d126b..0000000
--- a/src/main/java/io/api/etherscan/model/Log.java
+++ /dev/null
@@ -1,169 +0,0 @@
-package io.api.etherscan.model;
-
-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;
-    private Long _blockNumber;
-    private String address;
-    private String transactionHash;
-    private String transactionIndex;
-    private Long _transactionIndex;
-    private String timeStamp;
-    private LocalDateTime _timeStamp;
-    private String data;
-    private String gasPrice;
-    private BigInteger _gasPrice;
-    private String gasUsed;
-    private BigInteger _gasUsed;
-    private List<String> topics;
-    private String logIndex;
-    private Long _logIndex;
-
-    // <editor-fold desc="Getters">
-    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<String> getTopics() {
-        return topics;
-    }
-
-    public Long getLogIndex() {
-        if (_logIndex == null && !BasicUtils.isEmpty(logIndex)) {
-            _logIndex = BasicUtils.parseHex(logIndex).longValue();
-        }
-        return _logIndex;
-    }
-    // </editor-fold>
-
-    @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 d2c6d1c..0000000
--- a/src/main/java/io/api/etherscan/model/Price.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package io.api.etherscan.model;
-
-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;
-    private LocalDateTime _ethusd_timestamp;
-    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;
-
-    // <editor-fold desc="Getters">
-    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;
-    }
-    // </editor-fold>
-
-    @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 22c5104..0000000
--- a/src/main/java/io/api/etherscan/model/TxInternal.java
+++ /dev/null
@@ -1,69 +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 long traceId;
-    private int isError;
-    private String errCode;
-
-    // <editor-fold desc="Getters">
-    public String getType() {
-        return type;
-    }
-
-    public long getTraceId() {
-        return traceId;
-    }
-
-    public boolean haveError() {
-        return isError == 1;
-    }
-
-    public String getErrCode() {
-        return errCode;
-    }
-    // </editor-fold>
-
-    @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 (traceId != that.traceId)
-            return false;
-        return Objects.equals(errCode, that.errCode);
-    }
-
-    @Override
-    public int hashCode() {
-        int result = super.hashCode();
-        result = 31 * result + (int) (traceId ^ (traceId >>> 32));
-        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;
-
-    // <editor-fold desc="Getters">
-    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;
-    }
-    // </editor-fold>
-
-    @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;
-
-    // <editor-fold desc="Getters">
-    public String getMiner() {
-        return miner;
-    }
-
-    public BigInteger getBlockreward() {
-        return blockreward;
-    }
-
-    public int getUnclePosition() {
-        return unclePosition;
-    }
-    // </editor-fold>
-
-    @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<Uncle> uncles;
-    private String uncleInclusionReward;
-
-    // <editor-fold desc="Getters">
-    public boolean isEmpty() {
-        return getBlockNumber() == 0 && getBlockReward() == null
-                && getTimeStamp() == null
-                && BasicUtils.isEmpty(blockMiner);
-    }
-
-    public String getBlockMiner() {
-        return blockMiner;
-    }
-
-    public List<Uncle> getUncles() {
-        return uncles;
-    }
-
-    public String getUncleInclusionReward() {
-        return uncleInclusionReward;
-    }
-    // </editor-fold>
-
-    @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;
-    }
-
-    // <editor-fold desc="Getters">
-    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));
-    }
-    // </editor-fold>
-
-    @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 3d7ddd3..0000000
--- a/src/main/java/io/api/etherscan/model/proxy/BlockProxy.java
+++ /dev/null
@@ -1,193 +0,0 @@
-package io.api.etherscan.model.proxy;
-
-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;
-    private Long _number;
-    private String hash;
-    private String parentHash;
-    private String stateRoot;
-    private String size;
-    private Long _size;
-    private String difficulty;
-    private String totalDifficulty;
-    private String timestamp;
-    private LocalDateTime _timestamp;
-
-    private String miner;
-    private String nonce;
-    private String extraData;
-    private String logsBloom;
-    private String mixHash;
-    private String gasUsed;
-    private BigInteger _gasUsed;
-    private String gasLimit;
-    private BigInteger _gasLimit;
-
-    private String sha3Uncles;
-    private List<String> uncles;
-
-    private String receiptsRoot;
-    private String transactionsRoot;
-    private List<TxProxy> transactions;
-
-    // <editor-fold desc="Getters">
-    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<String> getUncles() {
-        return uncles;
-    }
-
-    public String getReceiptsRoot() {
-        return receiptsRoot;
-    }
-
-    public String getTransactionsRoot() {
-        return transactionsRoot;
-    }
-
-    public List<TxProxy> getTransactions() {
-        return transactions;
-    }
-    // </editor-fold>
-
-    @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 d69a627..0000000
--- a/src/main/java/io/api/etherscan/model/proxy/ReceiptProxy.java
+++ /dev/null
@@ -1,138 +0,0 @@
-package io.api.etherscan.model.proxy;
-
-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;
-    private Long _blockNumber;
-    private String blockHash;
-    private String transactionHash;
-    private String transactionIndex;
-    private Long _transactionIndex;
-    private String gasUsed;
-    private BigInteger _gasUsed;
-    private String cumulativeGasUsed;
-    private BigInteger _cumulativeGasUsed;
-    private String contractAddress;
-
-    private List<Log> logs;
-    private String logsBloom;
-
-    // <editor-fold desc="Getters">
-    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<Log> getLogs() {
-        return logs;
-    }
-
-    public String getLogsBloom() {
-        return logsBloom;
-    }
-    // </editor-fold>
-
-    @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 25b50c8..0000000
--- a/src/main/java/io/api/etherscan/model/proxy/TxProxy.java
+++ /dev/null
@@ -1,151 +0,0 @@
-package io.api.etherscan.model.proxy;
-
-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;
-    private Long _transactionIndex;
-    private String from;
-    private String v;
-    private String input;
-    private String s;
-    private String r;
-    private String nonce;
-    private Long _nonce;
-    private String value;
-    private String gas;
-    private BigInteger _gas;
-    private String gasPrice;
-    private BigInteger _gasPrice;
-    private String blockHash;
-    private String blockNumber;
-    private Long _blockNumber;
-
-    // <editor-fold desc="Getters">
-    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;
-    }
-    // </editor-fold>
-
-    @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/proxy/utility/BaseProxyTO.java b/src/main/java/io/api/etherscan/model/proxy/utility/BaseProxyTO.java
deleted file mode 100644
index 52c886f..0000000
--- a/src/main/java/io/api/etherscan/model/proxy/utility/BaseProxyTO.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package io.api.etherscan.model.proxy.utility;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 31.10.2018
- */
-abstract class BaseProxyTO {
-
-    private String id;
-    private String jsonrpc;
-    private ErrorProxyTO error;
-
-    public String getId() {
-        return id;
-    }
-
-    public String getJsonrpc() {
-        return jsonrpc;
-    }
-
-    public ErrorProxyTO getError() {
-        return error;
-    }
-}
diff --git a/src/main/java/io/api/etherscan/model/proxy/utility/BlockProxyTO.java b/src/main/java/io/api/etherscan/model/proxy/utility/BlockProxyTO.java
deleted file mode 100644
index eb9d941..0000000
--- a/src/main/java/io/api/etherscan/model/proxy/utility/BlockProxyTO.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package io.api.etherscan.model.proxy.utility;
-
-import io.api.etherscan.model.proxy.BlockProxy;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 01.11.2018
- */
-public class BlockProxyTO extends BaseProxyTO {
-
-    private BlockProxy result;
-
-    public BlockProxy getResult() {
-        return result;
-    }
-}
diff --git a/src/main/java/io/api/etherscan/model/proxy/utility/ErrorProxyTO.java b/src/main/java/io/api/etherscan/model/proxy/utility/ErrorProxyTO.java
deleted file mode 100644
index 57d2c07..0000000
--- a/src/main/java/io/api/etherscan/model/proxy/utility/ErrorProxyTO.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package io.api.etherscan.model.proxy.utility;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 03.11.2018
- */
-public class ErrorProxyTO {
-
-    private String message;
-    private String code;
-
-    public String getMessage() {
-        return message;
-    }
-
-    public String getCode() {
-        return code;
-    }
-}
diff --git a/src/main/java/io/api/etherscan/model/proxy/utility/StringProxyTO.java b/src/main/java/io/api/etherscan/model/proxy/utility/StringProxyTO.java
deleted file mode 100644
index 90cd7c8..0000000
--- a/src/main/java/io/api/etherscan/model/proxy/utility/StringProxyTO.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package io.api.etherscan.model.proxy.utility;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 31.10.2018
- */
-public class StringProxyTO extends BaseProxyTO {
-
-    private String result;
-
-    public String getResult() {
-        return result;
-    }
-}
diff --git a/src/main/java/io/api/etherscan/model/proxy/utility/TxInfoProxyTO.java b/src/main/java/io/api/etherscan/model/proxy/utility/TxInfoProxyTO.java
deleted file mode 100644
index c709f76..0000000
--- a/src/main/java/io/api/etherscan/model/proxy/utility/TxInfoProxyTO.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package io.api.etherscan.model.proxy.utility;
-
-import io.api.etherscan.model.proxy.ReceiptProxy;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 03.11.2018
- */
-public class TxInfoProxyTO extends BaseProxyTO {
-
-    private ReceiptProxy result;
-
-    public ReceiptProxy getResult() {
-        return result;
-    }
-}
diff --git a/src/main/java/io/api/etherscan/model/proxy/utility/TxProxyTO.java b/src/main/java/io/api/etherscan/model/proxy/utility/TxProxyTO.java
deleted file mode 100644
index 4140a62..0000000
--- a/src/main/java/io/api/etherscan/model/proxy/utility/TxProxyTO.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package io.api.etherscan.model.proxy.utility;
-
-import io.api.etherscan.model.proxy.TxProxy;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 01.11.2018
- */
-public class TxProxyTO extends BaseProxyTO {
-
-    private TxProxy result;
-
-    public TxProxy getResult() {
-        return result;
-    }
-}
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/query/impl/LogTopicQuadro.java b/src/main/java/io/api/etherscan/model/query/impl/LogTopicQuadro.java
deleted file mode 100644
index 1c2bf35..0000000
--- a/src/main/java/io/api/etherscan/model/query/impl/LogTopicQuadro.java
+++ /dev/null
@@ -1,94 +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.model.query.LogOp;
-
-/**
- * Quadro topic parameter builder for The Event Log API
- *
- * @see LogQueryBuilder
- * @see ILogsApi
- *
- * @author GoodforGod
- * @since 31.10.2018
- */
-public class LogTopicQuadro extends BaseLogQuery implements IQueryBuilder {
-
-    private final String address;
-    private final long startBlock, endBlock;
-    private final String topic0, topic1, topic2, topic3;
-
-    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) {
-        this.address = address;
-        this.startBlock = startBlock;
-        this.endBlock = endBlock;
-        this.topic0 = topic0;
-        this.topic1 = topic1;
-        this.topic2 = topic2;
-        this.topic3 = topic3;
-    }
-
-    public LogTopicQuadro setOpTopic0_1(LogOp topic0_1_opr) {
-        this.topic0_1_opr = topic0_1_opr;
-        return this;
-    }
-
-    public LogTopicQuadro setOpTopic1_2(LogOp topic1_2_opr) {
-        this.topic1_2_opr = topic1_2_opr;
-        return this;
-    }
-
-    public LogTopicQuadro setOpTopic2_3(LogOp topic2_3_opr) {
-        this.topic2_3_opr = topic2_3_opr;
-        return this;
-    }
-
-    public LogTopicQuadro setOpTopic0_2(LogOp topic0_2_opr) {
-        this.topic0_2_opr = topic0_2_opr;
-        return this;
-    }
-
-    public LogTopicQuadro setOpTopic0_3(LogOp topic0_3_opr) {
-        this.topic0_3_opr = topic0_3_opr;
-        return this;
-    }
-
-    public LogTopicQuadro setOpTopic1_3(LogOp topic1_3_opr) {
-        this.topic1_3_opr = topic1_3_opr;
-        return this;
-    }
-
-    @Override
-    public LogQuery build() {
-        if (topic0_1_opr == null)
-            throw new LogQueryException("topic0_1_opr can not be null.");
-        if (topic0_2_opr == null)
-            throw new LogQueryException("topic0_2_opr can not be null.");
-        if (topic0_3_opr == null)
-            throw new LogQueryException("topic0_3_opr can not be null.");
-        if (topic1_2_opr == null)
-            throw new LogQueryException("topic1_2_opr can not be null.");
-        if (topic2_3_opr == null)
-            throw new LogQueryException("topic2_3_opr can not be null.");
-        if (topic1_3_opr == null)
-            throw new LogQueryException("topic1_3_opr can not be null.");
-
-        return new LogQuery(ADDRESS_PARAM + address
-                + FROM_BLOCK_PARAM + startBlock + TO_BLOCK_PARAM + endBlock
-                + TOPIC_0_PARAM + topic0
-                + TOPIC_1_PARAM + topic1
-                + TOPIC_2_PARAM + topic2
-                + TOPIC_3_PARAM + topic3
-                + TOPIC_0_1_OPR_PARAM + topic0_1_opr.getOperation()
-                + TOPIC_0_2_OPR_PARAM + topic0_2_opr.getOperation()
-                + TOPIC_0_3_OPR_PARAM + topic0_2_opr.getOperation()
-                + TOPIC_1_2_OPR_PARAM + topic0_2_opr.getOperation()
-                + TOPIC_1_3_OPR_PARAM + topic1_2_opr.getOperation()
-                + TOPIC_2_3_OPR_PARAM + topic0_2_opr.getOperation());
-    }
-}
diff --git a/src/main/java/io/api/etherscan/model/query/impl/LogTopicSingle.java b/src/main/java/io/api/etherscan/model/query/impl/LogTopicSingle.java
deleted file mode 100644
index 2c19d61..0000000
--- a/src/main/java/io/api/etherscan/model/query/impl/LogTopicSingle.java
+++ /dev/null
@@ -1,36 +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;
-
-/**
- * Single topic parameter builder for The Event Log API
- *
- * @see LogQueryBuilder
- * @see ILogsApi
- *
- * @author GoodforGod
- * @since 31.10.2018
- */
-public class LogTopicSingle extends BaseLogQuery implements IQueryBuilder {
-
-    private final String address;
-    private final long startBlock, endBlock;
-
-    private final String topic0;
-
-    LogTopicSingle(String address, long startBlock, long endBlock, String topic0) {
-        this.address = address;
-        this.startBlock = startBlock;
-        this.endBlock = endBlock;
-        this.topic0 = topic0;
-    }
-
-    @Override
-    public LogQuery build() throws LogQueryException {
-        return new LogQuery(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/api/etherscan/model/query/impl/LogTopicTriple.java
deleted file mode 100644
index aa54740..0000000
--- a/src/main/java/io/api/etherscan/model/query/impl/LogTopicTriple.java
+++ /dev/null
@@ -1,68 +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.model.query.LogOp;
-
-/**
- * Triple topic parameter builder for The Event Log API
- *
- * @see LogQueryBuilder
- * @see ILogsApi
- *
- * @author GoodforGod
- * @since 31.10.2018
- */
-public class LogTopicTriple extends BaseLogQuery implements IQueryBuilder {
-
-    private final String address;
-    private final long startBlock, endBlock;
-    private final String topic0, topic1, topic2;
-
-    private LogOp topic0_1_opr, topic1_2_opr, topic0_2_opr;
-
-    LogTopicTriple(String address, long startBlock, long endBlock,
-                   String topic0, String topic1, String topic2) {
-        this.address = address;
-        this.startBlock = startBlock;
-        this.endBlock = endBlock;
-        this.topic0 = topic0;
-        this.topic1 = topic1;
-        this.topic2 = topic2;
-    }
-
-    public LogTopicTriple setOpTopic0_1(LogOp topic0_1_opr) {
-        this.topic0_1_opr = topic0_1_opr;
-        return this;
-    }
-
-    public LogTopicTriple setOpTopic0_2(LogOp topic0_2_opr) {
-        this.topic0_2_opr = topic0_2_opr;
-        return this;
-    }
-
-    public LogTopicTriple setOpTopic1_2(LogOp topic1_2_opr) {
-        this.topic1_2_opr = topic1_2_opr;
-        return this;
-    }
-
-    @Override
-    public LogQuery build() throws LogQueryException {
-        if (topic0_1_opr == null)
-            throw new LogQueryException("topic0_1_opr can not be null.");
-        if (topic0_2_opr == null)
-            throw new LogQueryException("topic0_2_opr can not be null.");
-        if (topic1_2_opr == null)
-            throw new LogQueryException("topic1_2_opr can not be null.");
-
-        return new LogQuery(ADDRESS_PARAM + address
-                + FROM_BLOCK_PARAM + startBlock + TO_BLOCK_PARAM + endBlock
-                + TOPIC_0_PARAM + topic0
-                + TOPIC_1_PARAM + topic1
-                + TOPIC_2_PARAM + topic2
-                + TOPIC_0_1_OPR_PARAM + topic0_1_opr.getOperation()
-                + TOPIC_1_2_OPR_PARAM + topic1_2_opr.getOperation()
-                + TOPIC_0_2_OPR_PARAM + topic0_2_opr.getOperation());
-    }
-}
diff --git a/src/main/java/io/api/etherscan/model/query/impl/LogTopicTuple.java b/src/main/java/io/api/etherscan/model/query/impl/LogTopicTuple.java
deleted file mode 100644
index 8f069f1..0000000
--- a/src/main/java/io/api/etherscan/model/query/impl/LogTopicTuple.java
+++ /dev/null
@@ -1,50 +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.model.query.LogOp;
-
-/**
- * Tuple topic parameter builder for The Event Log API
- *
- * @see LogQueryBuilder
- * @see ILogsApi
- *
- * @author GoodforGod
- * @since 31.10.2018
- */
-public class LogTopicTuple extends BaseLogQuery implements IQueryBuilder {
-
-    private final String address;
-    private final long startBlock, endBlock;
-    private final String topic0, topic1;
-
-    private LogOp topic0_1_opr;
-
-    LogTopicTuple(String address, long startBlock, long endBlock,
-                  String topic0, String topic1) {
-        this.address = address;
-        this.startBlock = startBlock;
-        this.endBlock = endBlock;
-        this.topic0 = topic0;
-        this.topic1 = topic1;
-    }
-
-    public LogTopicTuple setOpTopic0_1(LogOp topic0_1_opr) {
-        this.topic0_1_opr = topic0_1_opr;
-        return this;
-    }
-
-    @Override
-    public LogQuery build() throws LogQueryException {
-        if (topic0_1_opr == null)
-            throw new LogQueryException("topic0_1_opr can not be null.");
-
-        return new LogQuery(ADDRESS_PARAM + address
-                + FROM_BLOCK_PARAM + startBlock + TO_BLOCK_PARAM + endBlock
-                + TOPIC_0_PARAM + topic0
-                + TOPIC_1_PARAM + topic1
-                + TOPIC_0_1_OPR_PARAM + topic0_1_opr.getOperation());
-    }
-}
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<TxToken> {
-
-}
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 <a href="https://docs.etherscan.io/api-endpoints/accounts">...</a>
+ *
+ * @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<Balance> balances(@NotNull List<String> 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<Tx> txs(@NotNull String address, long startBlock, long endBlock) throws EtherScanException;
+
+    @NotNull
+    List<Tx> txs(@NotNull String address, long startBlock) throws EtherScanException;
+
+    @NotNull
+    List<Tx> 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<TxInternal> txsInternal(@NotNull String address, long startBlock, long endBlock) throws EtherScanException;
+
+    @NotNull
+    List<TxInternal> txsInternal(@NotNull String address, long startBlock) throws EtherScanException;
+
+    @NotNull
+    List<TxInternal> 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<TxInternal> 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<TxErc20> txsErc20(@NotNull String address, long startBlock, long endBlock) throws EtherScanException;
+
+    @NotNull
+    List<TxErc20> txsErc20(@NotNull String address, long startBlock) throws EtherScanException;
+
+    @NotNull
+    List<TxErc20> 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<TxErc20> txsErc20(@NotNull String address, @NotNull String contractAddress, long startBlock, long endBlock)
+            throws EtherScanException;
+
+    @NotNull
+    List<TxErc20> txsErc20(@NotNull String address, @NotNull String contractAddress, long startBlock) throws EtherScanException;
+
+    @NotNull
+    List<TxErc20> 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<TxErc721> txsErc721(@NotNull String address, long startBlock, long endBlock) throws EtherScanException;
+
+    @NotNull
+    List<TxErc721> txsErc721(@NotNull String address, long startBlock) throws EtherScanException;
+
+    @NotNull
+    List<TxErc721> 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<TxErc721> txsErc721(@NotNull String address, @NotNull String contractAddress, long startBlock, long endBlock)
+            throws EtherScanException;
+
+    @NotNull
+    List<TxErc721> txsErc721(@NotNull String address, @NotNull String contractAddress, long startBlock) throws EtherScanException;
+
+    @NotNull
+    List<TxErc721> 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<TxErc1155> txsErc1155(@NotNull String address, long startBlock, long endBlock) throws EtherScanException;
+
+    @NotNull
+    List<TxErc1155> txsErc1155(@NotNull String address, long startBlock) throws EtherScanException;
+
+    @NotNull
+    List<TxErc1155> 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<TxErc1155> txsErc1155(@NotNull String address, @NotNull String contractAddress, long startBlock, long endBlock)
+            throws EtherScanException;
+
+    @NotNull
+    List<TxErc1155> txsErc1155(@NotNull String address, @NotNull String contractAddress, long startBlock)
+            throws EtherScanException;
+
+    @NotNull
+    List<TxErc1155> 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<Block> 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..aceb9a2
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java
@@ -0,0 +1,350 @@
+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
+ */
+public class AccountAPIProvider extends BasicProvider implements AccountAPI {
+
+    private static final int OFFSET_MAX = 9999;
+
+    private static final String ACT_BALANCE_ACTION = ACT_PREFIX + "balance";
+    private static final String ACT_TOKEN_BALANCE_PARAM = ACT_PREFIX + "tokenbalance";
+    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=";
+
+    public AccountAPIProvider(RequestQueueManager requestQueueManager,
+                              String baseUrl,
+                              EthHttpClient executor,
+                              Converter converter,
+                              int retryCount) {
+        super(requestQueueManager, "account", baseUrl, executor, converter, retryCount);
+    }
+
+    @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 = getResponse(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 = getResponse(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<Balance> balances(@NotNull List<String> addresses) throws EtherScanException {
+        if (BasicUtils.isEmpty(addresses)) {
+            return Collections.emptyList();
+        }
+
+        BasicUtils.validateAddresses(addresses);
+
+        // Maximum addresses in batch request - 20
+        final List<Balance> balances = new ArrayList<>();
+        final List<List<String>> addressesAsBatches = BasicUtils.partition(addresses, 20);
+
+        for (final List<String> batch : addressesAsBatches) {
+            final String urlParams = ACT_BALANCE_MULTI_ACTION + TAG_LATEST_PARAM + ADDRESS_PARAM
+                    + BasicUtils.toAddressParam(batch);
+            final BalanceResponseTO response = getResponse(urlParams, BalanceResponseTO.class);
+            if (response.getStatus() != 1) {
+                throw new EtherScanResponseException(response);
+            }
+
+            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;
+    }
+
+    @NotNull
+    @Override
+    public List<Tx> txs(@NotNull String address) throws EtherScanException {
+        return txs(address, MIN_START_BLOCK);
+    }
+
+    @NotNull
+    @Override
+    public List<Tx> txs(@NotNull String address, long startBlock) throws EtherScanException {
+        return txs(address, startBlock, MAX_END_BLOCK);
+    }
+
+    @NotNull
+    @Override
+    public List<Tx> 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);
+    }
+
+    @NotNull
+    @Override
+    public List<TxInternal> txsInternal(@NotNull String address) throws EtherScanException {
+        return txsInternal(address, MIN_START_BLOCK);
+    }
+
+    @NotNull
+    @Override
+    public List<TxInternal> txsInternal(@NotNull String address, long startBlock) throws EtherScanException {
+        return txsInternal(address, startBlock, MAX_END_BLOCK);
+    }
+
+    @NotNull
+    @Override
+    public List<TxInternal> 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<TxInternal> txsInternalByHash(@NotNull String txhash) throws EtherScanException {
+        BasicUtils.validateTxHash(txhash);
+
+        final String urlParams = ACT_TX_INTERNAL_ACTION + TXHASH_PARAM + txhash;
+        final TxInternalResponseTO response = getResponse(urlParams, TxInternalResponseTO.class);
+        BasicUtils.validateTxResponse(response);
+
+        return BasicUtils.isEmpty(response.getResult())
+                ? Collections.emptyList()
+                : response.getResult();
+    }
+
+    @NotNull
+    @Override
+    public List<TxErc20> txsErc20(@NotNull String address) throws EtherScanException {
+        return txsErc20(address, MIN_START_BLOCK);
+    }
+
+    @NotNull
+    @Override
+    public List<TxErc20> txsErc20(@NotNull String address, long startBlock) throws EtherScanException {
+        return txsErc20(address, startBlock, MAX_END_BLOCK);
+    }
+
+    @NotNull
+    @Override
+    public List<TxErc20> 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<TxErc20> txsErc20(@NotNull String address, @NotNull String contractAddress) throws EtherScanException {
+        return txsErc20(address, contractAddress, MIN_START_BLOCK);
+    }
+
+    @NotNull
+    @Override
+    public List<TxErc20> txsErc20(@NotNull String address, @NotNull String contractAddress, long startBlock)
+            throws EtherScanException {
+        return txsErc20(address, contractAddress, startBlock, MAX_END_BLOCK);
+    }
+
+    @NotNull
+    @Override
+    public List<TxErc20> 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<TxErc721> txsErc721(@NotNull String address) throws EtherScanException {
+        return txsErc721(address, MIN_START_BLOCK);
+    }
+
+    @NotNull
+    @Override
+    public List<TxErc721> txsErc721(@NotNull String address, long startBlock) throws EtherScanException {
+        return txsErc721(address, startBlock, MAX_END_BLOCK);
+    }
+
+    @NotNull
+    @Override
+    public List<TxErc721> 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<TxErc721>
+            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<TxErc721> txsErc721(@NotNull String address, @NotNull String contractAddress, long startBlock)
+            throws EtherScanException {
+        return txsErc721(address, contractAddress, startBlock, MAX_END_BLOCK);
+    }
+
+    @Override
+    public @NotNull List<TxErc721> txsErc721(@NotNull String address, @NotNull String contractAddress) throws EtherScanException {
+        return txsErc721(address, contractAddress, MIN_START_BLOCK);
+    }
+
+    @Override
+    public @NotNull List<TxErc1155> 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<TxErc1155> txsErc1155(@NotNull String address, long startBlock) throws EtherScanException {
+        return txsErc1155(address, startBlock, MAX_END_BLOCK);
+    }
+
+    @Override
+    public @NotNull List<TxErc1155> txsErc1155(@NotNull String address) throws EtherScanException {
+        return txsErc1155(address, MIN_START_BLOCK);
+    }
+
+    @Override
+    public @NotNull List<TxErc1155>
+            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<TxErc1155> txsErc1155(@NotNull String address, @NotNull String contractAddress, long startBlock)
+            throws EtherScanException {
+        return txsErc1155(address, contractAddress, startBlock, MAX_END_BLOCK);
+    }
+
+    @Override
+    public @NotNull List<TxErc1155> txsErc1155(@NotNull String address, @NotNull String contractAddress)
+            throws EtherScanException {
+        return txsErc1155(address, contractAddress, MIN_START_BLOCK);
+    }
+
+    @NotNull
+    @Override
+    public List<Block> 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..0ab4f20
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java
@@ -0,0 +1,171 @@
+package io.goodforgod.api.etherscan;
+
+import io.goodforgod.api.etherscan.error.EtherScanException;
+import io.goodforgod.api.etherscan.error.EtherScanParseException;
+import io.goodforgod.api.etherscan.error.EtherScanRateLimitException;
+import io.goodforgod.api.etherscan.error.EtherScanResponseException;
+import io.goodforgod.api.etherscan.http.EthHttpClient;
+import io.goodforgod.api.etherscan.http.EthResponse;
+import io.goodforgod.api.etherscan.manager.RequestQueueManager;
+import io.goodforgod.api.etherscan.model.response.BaseListResponseTO;
+import io.goodforgod.api.etherscan.model.response.StringResponseTO;
+import io.goodforgod.api.etherscan.util.BasicUtils;
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+import org.jetbrains.annotations.ApiStatus.Internal;
+
+/**
+ * Base provider for API Implementations
+ *
+ * @author GoodforGod
+ * @see EtherScanAPIProvider
+ * @since 28.10.2018
+ */
+@Internal
+public abstract class BasicProvider {
+
+    private static final String MAX_RATE_LIMIT_REACHED = "Max rate limit reached";
+
+    private static final int OFFSET_MAX = 9999;
+
+    static final int MAX_END_BLOCK = Integer.MAX_VALUE;
+    static final int MIN_START_BLOCK = 0;
+
+    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;
+    private final int retryCountLimit;
+
+    public BasicProvider(RequestQueueManager requestQueueManager,
+                         String module,
+                         String baseUrl,
+                         EthHttpClient ethHttpClient,
+                         Converter converter,
+                         int retryCountLimit) {
+        this.queue = requestQueueManager;
+        this.module = "&module=" + module;
+        this.baseUrl = baseUrl;
+        this.executor = ethHttpClient;
+        this.converter = converter;
+        this.retryCountLimit = retryCountLimit;
+    }
+
+    protected <T> T convert(byte[] json, Class<T> 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);
+        }
+    }
+
+    protected int getMaximumOffset() {
+        return OFFSET_MAX;
+    }
+
+    /**
+     * Generic search for txs using offset api param To avoid 10k limit per response
+     *
+     * @param urlParams Url params for #getRequest()
+     * @param tClass    responseListTO class
+     * @param <T>       responseTO list T type
+     * @param <R>       responseListTO type
+     * @return List of T values
+     */
+    protected <T, R extends BaseListResponseTO<T>> List<T> getRequestUsingOffset(final String urlParams, Class<R> tClass)
+            throws EtherScanException {
+        final List<T> result = new ArrayList<>();
+        int page = 1;
+        while (true) {
+            final String formattedUrl = String.format(urlParams, page++);
+            final R response = getResponse(formattedUrl, tClass);
+            BasicUtils.validateTxResponse(response);
+            if (BasicUtils.isEmpty(response.getResult()))
+                break;
+
+            result.addAll(response.getResult());
+            if (response.getResult().size() < getMaximumOffset())
+                break;
+        }
+
+        return result;
+    }
+
+    protected EthResponse getResponse(String urlParameters) {
+        queue.takeTurn();
+        final URI uri = URI.create(baseUrl + module + urlParameters);
+        return executor.get(uri);
+    }
+
+    protected EthResponse postRequest(String urlParameters, String dataToPost) {
+        queue.takeTurn();
+        final URI uri = URI.create(baseUrl + module + urlParameters);
+        return executor.post(uri, dataToPost.getBytes(StandardCharsets.UTF_8));
+    }
+
+    protected <T> T getResponse(String urlParameters, Class<T> tClass) {
+        return getResponse(urlParameters, tClass, 0);
+    }
+
+    protected <T> T getResponse(String urlParameters, Class<T> tClass, int retryCount) {
+        try {
+            EthResponse response = getResponse(urlParameters);
+            return convert(response.body(), tClass);
+        } catch (Exception e) {
+            if (retryCount < retryCountLimit) {
+                try {
+                    Thread.sleep(1150);
+                } catch (InterruptedException ex) {
+                    throw new IllegalStateException(ex);
+                }
+
+                return getResponse(urlParameters, tClass, retryCount + 1);
+            } else {
+                throw e;
+            }
+        }
+    }
+
+    protected <T> T postRequest(String urlParameters, String dataToPost, Class<T> tClass) {
+        return postRequest(urlParameters, dataToPost, tClass, 0);
+    }
+
+    protected <T> T postRequest(String urlParameters, String dataToPost, Class<T> tClass, int retryCount) {
+        try {
+            EthResponse response = postRequest(urlParameters, dataToPost);
+            return convert(response.body(), tClass);
+        } catch (EtherScanRateLimitException e) {
+            if (retryCount < retryCountLimit) {
+                try {
+                    Thread.sleep(1150);
+                } catch (InterruptedException ex) {
+                    throw new IllegalStateException(ex);
+                }
+
+                return postRequest(urlParameters, dataToPost, tClass, retryCount + 1);
+            } else {
+                throw e;
+            }
+        }
+    }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/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 <a href="https://docs.etherscan.io/api-endpoints/blocks">...</a>
+ *
+ * @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<BlockUncle> 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..7228943
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java
@@ -0,0 +1,56 @@
+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
+ */
+public class BlockAPIProvider extends BasicProvider implements BlockAPI {
+
+    private static final String ACT_BLOCK_PARAM = ACT_PREFIX + "getblockreward";
+
+    private static final String BLOCKNO_PARAM = "&blockno=";
+
+    public BlockAPIProvider(RequestQueueManager requestQueueManager,
+                            String baseUrl,
+                            EthHttpClient executor,
+                            Converter converter,
+                            int retryCount) {
+        super(requestQueueManager, "block", baseUrl, executor, converter, retryCount);
+    }
+
+    @NotNull
+    @Override
+    public Optional<BlockUncle> uncles(long blockNumber) throws EtherScanException {
+        final String urlParam = ACT_BLOCK_PARAM + BLOCKNO_PARAM + blockNumber;
+        try {
+            final UncleBlockResponseTO responseTO = getResponse(urlParam, 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..c076b74
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/ContractAPI.java
@@ -0,0 +1,35 @@
+package io.goodforgod.api.etherscan;
+
+import io.goodforgod.api.etherscan.error.EtherScanException;
+import io.goodforgod.api.etherscan.model.Abi;
+import io.goodforgod.api.etherscan.model.ContractCreation;
+import java.util.List;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * EtherScan - API Descriptions <a href="https://docs.etherscan.io/api-endpoints/contracts">...</a>
+ *
+ * @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;
+
+    /**
+     * Returns a contract's deployer address and transaction hash it was created, up to 5 at a time.
+     * 
+     * @param contractAddresses - list of addresses to fetch
+     * @throws EtherScanException parent exception class
+     */
+    @NotNull
+    List<ContractCreation> contractCreation(@NotNull List<String> contractAddresses) throws EtherScanException;
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java
new file mode 100644
index 0000000..0719569
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java
@@ -0,0 +1,78 @@
+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.ContractCreation;
+import io.goodforgod.api.etherscan.model.response.ContractCreationResponseTO;
+import io.goodforgod.api.etherscan.model.response.StringResponseTO;
+import io.goodforgod.api.etherscan.util.BasicUtils;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Contract API Implementation
+ *
+ * @see ContractAPI
+ * @author GoodforGod
+ * @since 28.10.2018
+ */
+public class ContractAPIProvider extends BasicProvider implements ContractAPI {
+
+    private static final String ACT_ABI_PARAM = ACT_PREFIX + "getabi";
+
+    private static final String ADDRESS_PARAM = "&address=";
+
+    private static final String ACT_CONTRACT_CREATION_PARAM = "getcontractcreation";
+
+    private static final String ACT_CONTRACT_CREATION = ACT_PREFIX + ACT_CONTRACT_CREATION_PARAM;
+
+    private static final String ACT_CONTRACT_ADDRESSES_PARAM = "&contractaddresses=";
+
+    public ContractAPIProvider(RequestQueueManager requestQueueManager,
+                               String baseUrl,
+                               EthHttpClient executor,
+                               Converter converter,
+                               int retryCount) {
+        super(requestQueueManager, "contract", baseUrl, executor, converter, retryCount);
+    }
+
+    @NotNull
+    @Override
+    public Abi contractAbi(@NotNull String address) throws EtherScanException {
+        BasicUtils.validateAddress(address);
+
+        final String urlParam = ACT_ABI_PARAM + ADDRESS_PARAM + address;
+        final StringResponseTO response = getResponse(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());
+    }
+
+    @NotNull
+    @Override
+    public List<ContractCreation> contractCreation(@NotNull List<String> contractAddresses) throws EtherScanException {
+        BasicUtils.validateAddresses(contractAddresses);
+        final String urlParam = ACT_CONTRACT_CREATION + ACT_CONTRACT_ADDRESSES_PARAM
+                + BasicUtils.toAddressParam(contractAddresses);
+        final ContractCreationResponseTO response = getResponse(urlParam, ContractCreationResponseTO.class);
+        if (response.getStatus() != 1 && response.getMessage().startsWith("NOTOK")) {
+            throw new EtherScanResponseException(response);
+        }
+
+        return response.getResult().stream()
+                .map(to -> ContractCreation.builder()
+                        .withContractCreator(to.getContractCreator())
+                        .withContractAddress(to.getContractAddress())
+                        .withTxHash(to.getTxHash())
+                        .build())
+                .collect(Collectors.toList());
+    }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/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> T fromJson(byte[] jsonAsByteArray, @NotNull Class<T> 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 <a href="https://api.etherscan.io/api">EtherScan API</a>
+     */
+    @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..fa8af66
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java
@@ -0,0 +1,113 @@
+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.JdkEthHttpClient;
+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.Objects;
+import java.util.function.Supplier;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author Anton Kurako (GoodforGod)
+ * @since 11.05.2023
+ */
+public class EthScanAPIBuilder implements EtherScanAPI.Builder {
+
+    private static final Supplier<EthHttpClient> DEFAULT_SUPPLIER = JdkEthHttpClient::new;
+
+    private final Gson gson = new GsonConfiguration().builder().create();
+
+    private int retryCountOnLimitReach = 0;
+    private String apiKey;
+    private RequestQueueManager queueManager;
+    private EthNetwork ethNetwork = EthNetworks.MAINNET;
+    private Supplier<EthHttpClient> ethHttpClientSupplier = DEFAULT_SUPPLIER;
+    private Supplier<Converter> converterSupplier = () -> new Converter() {
+
+        @Override
+        public <T> @NotNull T fromJson(byte[] jsonAsByteArray, @NotNull Class<T> 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));
+            }
+        }
+    };
+
+    public EthScanAPIBuilder(String apiKey) {
+        this.apiKey = apiKey;
+    }
+
+    @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<EthHttpClient> httpClientSupplier) {
+        this.ethHttpClientSupplier = httpClientSupplier;
+        return this;
+    }
+
+    @NotNull
+    @Override
+    public EtherScanAPI.Builder withConverter(@NotNull Supplier<Converter> converterSupplier) {
+        this.converterSupplier = converterSupplier;
+        return this;
+    }
+
+    @NotNull
+    public EtherScanAPI.Builder withRetryOnRateLimit(int maxRetryCount) {
+        if (maxRetryCount < 0 || maxRetryCount > 20) {
+            throw new IllegalStateException("maxRetryCount value must be in range from 0 to 20, but was: " + maxRetryCount);
+        }
+
+        this.retryCountOnLimitReach = maxRetryCount;
+        return this;
+    }
+
+    @Override
+    public @NotNull EtherScanAPI build() {
+        RequestQueueManager requestQueueManager;
+        requestQueueManager = Objects.requireNonNullElseGet(queueManager, RequestQueueManager::planFree);
+
+        return new EtherScanAPIProvider(apiKey, ethNetwork, requestQueueManager, ethHttpClientSupplier.get(),
+                converterSupplier.get(), retryCountOnLimitReach);
+    }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java
new file mode 100644
index 0000000..4c703bb
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java
@@ -0,0 +1,79 @@
+package io.goodforgod.api.etherscan;
+
+import io.goodforgod.api.etherscan.error.EtherScanRateLimitException;
+import io.goodforgod.api.etherscan.http.EthHttpClient;
+import io.goodforgod.api.etherscan.manager.RequestQueueManager;
+import java.util.function.Supplier;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Range;
+
+/**
+ * EtherScan full API Description <a href="https://etherscan.io/apis">...</a>
+ *
+ * @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(@NotNull String key) {
+        return new EthScanAPIBuilder(key);
+    }
+
+    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<EthHttpClient> httpClientSupplier);
+
+        @NotNull
+        Builder withConverter(@NotNull Supplier<Converter> converterSupplier);
+
+        /**
+         * By default is disabled
+         *
+         * @param maxRetryCount to retry if {@link EtherScanRateLimitException} thrown
+         * @return self
+         */
+        @NotNull
+        EtherScanAPI.Builder withRetryOnRateLimit(@Range(from = 0, to = 20) int maxRetryCount);
+
+        @NotNull
+        EtherScanAPI build();
+    }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/EtherScanAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPIProvider.java
new file mode 100644
index 0000000..daf1e4a
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPIProvider.java
@@ -0,0 +1,96 @@
+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 <a href="https://etherscan.io/apis">...</a>
+ *
+ * @author GoodforGod
+ * @since 28.10.2018
+ */
+public 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;
+
+    public EtherScanAPIProvider(String apiKey,
+                                EthNetwork network,
+                                RequestQueueManager queue,
+                                EthHttpClient ethHttpClient,
+                                Converter converter,
+                                int retryCount) {
+        // EtherScan 1request\5sec limit support by queue manager
+        final String baseUrl = network.domain() + "?apikey=" + apiKey;
+
+        this.requestQueueManager = queue;
+        this.account = new AccountAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
+        this.block = new BlockAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
+        this.contract = new ContractAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
+        this.logs = new LogsAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
+        this.proxy = new ProxyAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
+        this.stats = new StatisticAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
+        this.txs = new TransactionAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
+        this.gasTracker = new GasTrackerAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
+    }
+
+    @NotNull
+    @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
+ * <a href="https://docs.etherscan.io/api-endpoints/gas-tracker">...</a>
+ *
+ * @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..e7ecb7c
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java
@@ -0,0 +1,55 @@
+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
+ */
+public class GasTrackerAPIProvider extends BasicProvider implements GasTrackerAPI {
+
+    private static final String ACT_GAS_ORACLE_PARAM = ACT_PREFIX + "gasoracle";
+    private static final String ACT_GAS_ESTIMATE_PARAM = ACT_PREFIX + "gasestimate";
+
+    private static final String GASPRICE_PARAM = "&gasprice=";
+
+    public GasTrackerAPIProvider(RequestQueueManager queue,
+                                 String baseUrl,
+                                 EthHttpClient ethHttpClient,
+                                 Converter converter,
+                                 int retryCount) {
+        super(queue, "gastracker", baseUrl, ethHttpClient, converter, retryCount);
+    }
+
+    @Override
+    public @NotNull Duration estimate(@NotNull Wei wei) throws EtherScanException {
+        final String urlParams = ACT_GAS_ESTIMATE_PARAM + GASPRICE_PARAM + wei.asWei().toString();
+        final GasEstimateResponseTO response = getResponse(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 = getResponse(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 <a href="https://docs.etherscan.io/api-endpoints/logs">...</a>
+ *
+ * @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<Log> 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..1002dc8
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/LogsAPIProvider.java
@@ -0,0 +1,44 @@
+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
+ */
+public class LogsAPIProvider extends BasicProvider implements LogsAPI {
+
+    private static final String ACT_LOGS_PARAM = ACT_PREFIX + "getLogs";
+
+    public LogsAPIProvider(RequestQueueManager queue,
+                           String baseUrl,
+                           EthHttpClient executor,
+                           Converter converter,
+                           int retryCount) {
+        super(queue, "logs", baseUrl, executor, converter, retryCount);
+    }
+
+    @NotNull
+    @Override
+    public List<Log> logs(@NotNull LogQuery query) throws EtherScanException {
+        final String urlParams = ACT_LOGS_PARAM + query.params();
+        final LogResponseTO response = getResponse(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 59%
rename from src/main/java/io/api/etherscan/core/IProxyApi.java
rename to src/main/java/io/goodforgod/api/etherscan/ProxyAPI.java
index e57f6ec..30c4f96 100644
--- a/src/main/java/io/api/etherscan/core/IProxyApi.java
+++ b/src/main/java/io/goodforgod/api/etherscan/ProxyAPI.java
@@ -1,27 +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 org.jetbrains.annotations.NotNull;
-
-import java.math.BigInteger;
+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;
 
 /**
- * EtherScan - API Descriptions https://etherscan.io/apis#proxy
+ * EtherScan - API Descriptions
+ * <a href="https://docs.etherscan.io/api-endpoints/geth-parity-proxy">...</a>
  *
  * @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();
 
@@ -30,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<BlockProxy> block(long blockNo) throws ApiException;
+    Optional<BlockProxy> block(long blockNo) throws EtherScanException;
 
     /**
      * Returns information about a uncle by block number eth_getUncleByBlockNumberAndIndex
@@ -41,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<BlockProxy> blockUncle(long blockNo, long index) throws ApiException;
+    Optional<BlockProxy> blockUncle(long blockNo, long index) throws EtherScanException;
 
     /**
      * Returns the information about a transaction requested by transaction hash
@@ -52,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<TxProxy> tx(String txhash) throws ApiException;
+    Optional<TxProxy> tx(@NotNull String txhash) throws EtherScanException;
 
     /**
      * Returns information about a transaction by block number and transaction index position
@@ -64,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<TxProxy> tx(long blockNo, long index) throws ApiException;
+    Optional<TxProxy> tx(long blockNo, long index) throws EtherScanException;
 
     /**
      * Returns the number of transactions in a block from a block matching the given block number
@@ -75,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
@@ -94,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<String> txSendRaw(String hexEncodedTx) throws ApiException;
+    Optional<String> 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<ReceiptProxy> txReceipt(String txhash) throws ApiException;
+    Optional<ReceiptProxy> txReceipt(@NotNull String txhash) throws EtherScanException;
 
     /**
      * Executes a new message call immediately without creating a transaction on the block chain
@@ -116,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<String> call(String address, String data) throws ApiException;
+    Optional<String> 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<String> code(String address) throws ApiException;
+    Optional<String> code(@NotNull String address) throws EtherScanException;
 
     /**
      * (**experimental) Returns the value from a storage position at a given address eth_getStorageAt
@@ -137,19 +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<String> storageAt(String address, long position) throws ApiException;
+    Optional<String> 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,
@@ -157,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 57%
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..e35fd08 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 {
+public class ProxyAPIProvider extends BasicProvider implements ProxyAPI {
 
     private static final String ACT_BLOCKNO_PARAM = ACT_PREFIX + "eth_blockNumber";
     private static final String ACT_BY_BLOCKNO_PARAM = ACT_PREFIX + "eth_getBlockByNumber";
@@ -59,15 +57,17 @@ 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);
+    public ProxyAPIProvider(RequestQueueManager queue,
+                            String baseUrl,
+                            EthHttpClient executor,
+                            Converter converter,
+                            int retryCount) {
+        super(queue, "proxy", baseUrl, executor, converter, retryCount);
     }
 
     @Override
-    public long blockNoLast() throws ApiException {
-        final StringProxyTO response = getRequest(ACT_BLOCKNO_PARAM, StringProxyTO.class);
+    public long blockNoLast() throws EtherScanException {
+        final StringProxyTO response = getResponse(ACT_BLOCKNO_PARAM, StringProxyTO.class);
         return (BasicUtils.isEmpty(response.getResult()))
                 ? -1
                 : BasicUtils.parseHex(response.getResult()).longValue();
@@ -75,121 +75,130 @@ public long blockNoLast() throws ApiException {
 
     @NotNull
     @Override
-    public Optional<BlockProxy> block(final long blockNo) throws ApiException {
+    public Optional<BlockProxy> block(long blockNo) throws EtherScanException {
         final long compBlockNo = BasicUtils.compensateMinBlock(blockNo);
 
         final String urlParams = ACT_BY_BLOCKNO_PARAM + TAG_PARAM + compBlockNo + BOOLEAN_PARAM;
-        final BlockProxyTO response = getRequest(urlParams, BlockProxyTO.class);
+        final BlockProxyTO response = getResponse(urlParams, BlockProxyTO.class);
         return Optional.ofNullable(response.getResult());
     }
 
     @NotNull
     @Override
-    public Optional<BlockProxy> blockUncle(final long blockNo, final long index) throws ApiException {
+    public Optional<BlockProxy> blockUncle(long blockNo, long index) throws EtherScanException {
         final long compBlockNo = BasicUtils.compensateMinBlock(blockNo);
         final long compIndex = BasicUtils.compensateMinBlock(index);
 
         final String urlParams = ACT_UNCLE_BY_BLOCKNOINDEX_PARAM + TAG_PARAM
                 + "0x" + Long.toHexString(compBlockNo) + INDEX_PARAM + "0x" + Long.toHexString(compIndex);
-        final BlockProxyTO response = getRequest(urlParams, BlockProxyTO.class);
+        final BlockProxyTO response = getResponse(urlParams, BlockProxyTO.class);
         return Optional.ofNullable(response.getResult());
     }
 
     @NotNull
     @Override
-    public Optional<TxProxy> tx(final String txhash) throws ApiException {
+    public Optional<TxProxy> tx(@NotNull String txhash) throws EtherScanException {
         BasicUtils.validateTxHash(txhash);
 
         final String urlParams = ACT_TX_BY_HASH_PARAM + TXHASH_PARAM + txhash;
-        final TxProxyTO response = getRequest(urlParams, TxProxyTO.class);
+        final TxProxyTO response = getResponse(urlParams, TxProxyTO.class);
         return Optional.ofNullable(response.getResult());
     }
 
     @NotNull
     @Override
-    public Optional<TxProxy> tx(final long blockNo, final long index) throws ApiException {
+    public Optional<TxProxy> 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);
-        final TxProxyTO response = getRequest(urlParams, TxProxyTO.class);
+        final TxProxyTO response = getResponse(urlParams, TxProxyTO.class);
         return Optional.ofNullable(response.getResult());
     }
 
     @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);
+        final StringProxyTO response = getResponse(urlParams, StringProxyTO.class);
         return BasicUtils.parseHex(response.getResult()).intValue();
     }
 
     @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;
-        final StringProxyTO response = getRequest(urlParams, StringProxyTO.class);
+        final StringProxyTO response = getResponse(urlParams, StringProxyTO.class);
         return BasicUtils.parseHex(response.getResult()).intValue();
     }
 
     @Override
     @NotNull
-    public Optional<String> txSendRaw(final String hexEncodedTx) throws ApiException {
+    public Optional<String> 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<ReceiptProxy> txReceipt(final String txhash) throws ApiException {
+    public Optional<ReceiptProxy> txReceipt(@NotNull String txhash) throws EtherScanException {
         BasicUtils.validateTxHash(txhash);
 
         final String urlParams = ACT_TX_RECEIPT_PARAM + TXHASH_PARAM + txhash;
-        final TxInfoProxyTO response = getRequest(urlParams, TxInfoProxyTO.class);
+        final TxInfoProxyTO response = getResponse(urlParams, TxInfoProxyTO.class);
         return Optional.ofNullable(response.getResult());
     }
 
     @NotNull
     @Override
-    public Optional<String> call(final String address, final String data) throws ApiException {
+    public Optional<String> 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);
+        final StringProxyTO response = getResponse(urlParams, StringProxyTO.class);
         return Optional.ofNullable(response.getResult());
     }
 
     @NotNull
     @Override
-    public Optional<String> code(final String address) throws ApiException {
+    public Optional<String> code(@NotNull String address) throws EtherScanException {
         BasicUtils.validateAddress(address);
 
         final String urlParams = ACT_CODE_PARAM + ADDRESS_PARAM + address + TAG_LAST_PARAM;
-        final StringProxyTO response = getRequest(urlParams, StringProxyTO.class);
+        final StringProxyTO response = getResponse(urlParams, StringProxyTO.class);
         return Optional.ofNullable(response.getResult());
     }
 
     @NotNull
     @Override
-    public Optional<String> storageAt(final String address, final long position) throws ApiException {
+    public Optional<String> storageAt(@NotNull String address, long position) throws EtherScanException {
         BasicUtils.validateAddress(address);
         final long compPosition = BasicUtils.compensateMinBlock(position);
 
         final String urlParams = ACT_STORAGEAT_PARAM + ADDRESS_PARAM + address + POSITION_PARAM + compPosition + TAG_LAST_PARAM;
-        final StringProxyTO response = getRequest(urlParams, StringProxyTO.class);
+        final StringProxyTO response = getResponse(urlParams, StringProxyTO.class);
         return (BasicUtils.isEmpty(response.getResult()) || EMPTY_HEX.matcher(response.getResult()).matches())
                 ? Optional.empty()
                 : Optional.of(response.getResult());
@@ -197,29 +206,29 @@ public Optional<String> storageAt(final String address, final long position) thr
 
     @NotNull
     @Override
-    public BigInteger gasPrice() throws ApiException {
-        final StringProxyTO response = getRequest(ACT_GASPRICE_PARAM, StringProxyTO.class);
+    public Wei gasPrice() throws EtherScanException {
+        final StringProxyTO response = getResponse(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);
+        final StringProxyTO response = getResponse(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..3f48127
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java
@@ -0,0 +1,58 @@
+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 <a href="https://docs.etherscan.io/api-endpoints/stats-1">...</a>
+ *
+ * @author GoodforGod
+ * @since 30.10.2018
+ */
+public interface StatisticAPI {
+
+    /**
+     * ERC20 token total Supply
+     * <a href=
+     * "https://docs.etherscan.io/api-endpoints/tokens#get-erc20-token-totalsupply-by-contractaddress">EtherScan</a>
+     * Returns the current amount of an ERC-20 token in circulation.
+     *
+     * @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..006017a
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java
@@ -0,0 +1,82 @@
+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
+ */
+public class StatisticAPIProvider extends BasicProvider implements StatisticAPI {
+
+    private static final String ACT_SUPPLY_PARAM = ACT_PREFIX + "ethsupply";
+    private static final String ACT_SUPPLY2_PARAM = ACT_PREFIX + "ethsupply2";
+    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=";
+
+    public StatisticAPIProvider(RequestQueueManager queue,
+                                String baseUrl,
+                                EthHttpClient executor,
+                                Converter converter,
+                                int retry) {
+        super(queue, "stats", baseUrl, executor, converter, retry);
+    }
+
+    @NotNull
+    @Override
+    public Wei supply() throws EtherScanException {
+        final StringResponseTO response = getResponse(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 = getResponse(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 = getResponse(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 = getResponse(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 <a href="https://docs.etherscan.io/api-endpoints/stats">...</a>
  *
  * @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<Status> execStatus(String txhash) throws ApiException;
+    Optional<Status> 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<Boolean> receiptStatus(String txhash) throws ApiException;
+    Optional<Boolean> statusReceipt(@NotNull String txhash) throws EtherScanException;
 }
diff --git a/src/main/java/io/goodforgod/api/etherscan/TransactionAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/TransactionAPIProvider.java
new file mode 100644
index 0000000..61f2484
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/TransactionAPIProvider.java
@@ -0,0 +1,60 @@
+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 TransactionAPI
+ * @since 28.10.2018
+ */
+public class TransactionAPIProvider extends BasicProvider implements TransactionAPI {
+
+    private static final String ACT_EXEC_STATUS_PARAM = ACT_PREFIX + "getstatus";
+    private static final String ACT_RECEIPT_STATUS_PARAM = ACT_PREFIX + "gettxreceiptstatus";
+
+    private static final String TXHASH_PARAM = "&txhash=";
+
+    public TransactionAPIProvider(RequestQueueManager queue,
+                                  String baseUrl,
+                                  EthHttpClient executor,
+                                  Converter converter,
+                                  int retryCount) {
+        super(queue, "transaction", baseUrl, executor, converter, retryCount);
+    }
+
+    @NotNull
+    @Override
+    public Optional<Status> statusExec(@NotNull String txhash) throws EtherScanException {
+        BasicUtils.validateTxHash(txhash);
+
+        final String urlParams = ACT_EXEC_STATUS_PARAM + TXHASH_PARAM + txhash;
+        final StatusResponseTO response = getResponse(urlParams, StatusResponseTO.class);
+        BasicUtils.validateTxResponse(response);
+
+        return Optional.ofNullable(response.getResult());
+    }
+
+    @NotNull
+    @Override
+    public Optional<Boolean> statusReceipt(@NotNull String txhash) throws EtherScanException {
+        BasicUtils.validateTxHash(txhash);
+
+        final String urlParams = ACT_RECEIPT_STATUS_PARAM + TXHASH_PARAM + txhash;
+        final ReceiptStatusResponseTO response = getResponse(urlParams, ReceiptStatusResponseTO.class);
+        BasicUtils.validateTxResponse(response);
+
+        return (response.getResult() == null || BasicUtils.isEmpty(response.getResult().getStatus()))
+                ? Optional.empty()
+                : Optional.of(response.getResult().getStatus().contains("1"));
+    }
+}
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/EtherScanConnectionTimeoutException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanConnectionTimeoutException.java
new file mode 100644
index 0000000..1b2ff22
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanConnectionTimeoutException.java
@@ -0,0 +1,12 @@
+package io.goodforgod.api.etherscan.error;
+
+/**
+ * @author GoodforGod
+ * @since 12.11.2018
+ */
+public class EtherScanConnectionTimeoutException extends EtherScanConnectionException {
+
+    public EtherScanConnectionTimeoutException(String message, Throwable cause) {
+        super(message, cause);
+    }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/error/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/http/EthHttpClient.java b/src/main/java/io/goodforgod/api/etherscan/http/EthHttpClient.java
new file mode 100644
index 0000000..28142b8
--- /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
+     */
+    EthResponse get(@NotNull URI uri);
+
+    /**
+     * Performs a Http POST request
+     *
+     * @param uri  as string
+     * @param body to post
+     * @return result as string
+     */
+    EthResponse post(@NotNull URI uri, byte[] body);
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/http/EthResponse.java b/src/main/java/io/goodforgod/api/etherscan/http/EthResponse.java
new file mode 100644
index 0000000..20b7d81
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/http/EthResponse.java
@@ -0,0 +1,30 @@
+package io.goodforgod.api.etherscan.http;
+
+import java.util.List;
+import java.util.Map;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Anton Kurako (GoodforGod)
+ * 
+ * @since 07.09.2025
+ */
+public interface EthResponse {
+
+    int statusCode();
+
+    @Nullable
+    byte[] body();
+
+    @NotNull
+    Map<String, List<String>> headers();
+
+    static EthResponse of(int statusCode, @Nullable byte[] body) {
+        return new SimpleEthResponse(statusCode, body, null);
+    }
+
+    static EthResponse of(int statusCode, @Nullable byte[] body, @Nullable Map<String, List<String>> headers) {
+        return new SimpleEthResponse(statusCode, body, headers);
+    }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/http/SimpleEthResponse.java b/src/main/java/io/goodforgod/api/etherscan/http/SimpleEthResponse.java
new file mode 100644
index 0000000..2e9ebf4
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/http/SimpleEthResponse.java
@@ -0,0 +1,64 @@
+package io.goodforgod.api.etherscan.http;
+
+import java.util.*;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Anton Kurako (GoodforGod)
+ *
+ * @since 07.09.2025
+ */
+class SimpleEthResponse implements EthResponse {
+
+    private final int statusCode;
+    @Nullable
+    private final byte[] body;
+    private final Map<String, List<String>> headers;
+
+    SimpleEthResponse(int statusCode, @Nullable byte[] body, @Nullable Map<String, List<String>> headers) {
+        this.statusCode = statusCode;
+        this.body = body;
+        this.headers = (headers == null)
+                ? Collections.emptyMap()
+                : headers;
+    }
+
+    @Override
+    public int statusCode() {
+        return statusCode;
+    }
+
+    @Nullable
+    @Override
+    public byte[] body() {
+        return body;
+    }
+
+    @Override
+    public @NotNull Map<String, List<String>> headers() {
+        return headers;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        SimpleEthResponse that = (SimpleEthResponse) o;
+        return statusCode == that.statusCode && Arrays.equals(body, that.body) && Objects.equals(headers, that.headers);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = Objects.hash(statusCode, headers);
+        result = 31 * result + Arrays.hashCode(body);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return "{\"statusCode\": " + statusCode + ", \"headers\": " + headers + ", \"body\": \"" + Arrays.toString(body) + "\"}";
+    }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/http/impl/JdkEthHttpClient.java b/src/main/java/io/goodforgod/api/etherscan/http/impl/JdkEthHttpClient.java
new file mode 100644
index 0000000..01580ce
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/http/impl/JdkEthHttpClient.java
@@ -0,0 +1,186 @@
+package io.goodforgod.api.etherscan.http.impl;
+
+import io.goodforgod.api.etherscan.error.EtherScanConnectionException;
+import io.goodforgod.api.etherscan.error.EtherScanConnectionTimeoutException;
+import io.goodforgod.api.etherscan.http.EthHttpClient;
+import io.goodforgod.api.etherscan.http.EthResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UncheckedIOException;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpConnectTimeoutException;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.time.Duration;
+import java.util.*;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.InflaterInputStream;
+import org.jetbrains.annotations.ApiStatus.Internal;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Anton Kurako (GoodforGod)
+ *
+ * @since 07.09.2025
+ */
+@Internal
+public class JdkEthHttpClient implements EthHttpClient {
+
+    private static final Map<String, String> DEFAULT_HEADERS = new HashMap<>();
+
+    private static final Duration DEFAULT_CONNECT_TIMEOUT = Duration.ofSeconds(8);
+    private static final Duration DEFAULT_READ_TIMEOUT = Duration.ofMinutes(2);
+
+    static {
+        DEFAULT_HEADERS.put("Accept-Language", "en");
+        DEFAULT_HEADERS.put("Accept-Encoding", "deflate, gzip");
+        DEFAULT_HEADERS.put("User-Agent", "Chrome/68.0.3440.106");
+        DEFAULT_HEADERS.put("Accept-Charset", "UTF-8");
+    }
+
+    private final HttpClient httpClient;
+    private final Duration requestTimeout;
+    private final Map<String, String> headers;
+    private final UrlEthHttpClient urlEthHttpClient;
+
+    public JdkEthHttpClient() {
+        this(HttpClient.newBuilder()
+                .connectTimeout(DEFAULT_CONNECT_TIMEOUT)
+                .followRedirects(HttpClient.Redirect.NORMAL)
+                .version(HttpClient.Version.HTTP_2)
+                .build(),
+                DEFAULT_READ_TIMEOUT,
+                DEFAULT_HEADERS);
+    }
+
+    public JdkEthHttpClient(HttpClient httpClient) {
+        this(httpClient, DEFAULT_READ_TIMEOUT, DEFAULT_HEADERS);
+    }
+
+    public JdkEthHttpClient(HttpClient httpClient, Duration requestTimeout) {
+        this(httpClient, requestTimeout, DEFAULT_HEADERS);
+    }
+
+    public JdkEthHttpClient(HttpClient httpClient, Duration requestTimeout, Map<String, String> headers) {
+        this.httpClient = httpClient;
+        this.requestTimeout = requestTimeout;
+        this.headers = headers;
+        this.urlEthHttpClient = new UrlEthHttpClient(DEFAULT_CONNECT_TIMEOUT, requestTimeout, headers);
+    }
+
+    @Override
+    public EthResponse get(@NotNull URI uri) {
+        HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
+                .GET()
+                .uri(uri)
+                .timeout(requestTimeout);
+
+        headers.forEach(requestBuilder::header);
+
+        try {
+            HttpResponse<InputStream> response = httpClient.send(requestBuilder.build(),
+                    HttpResponse.BodyHandlers.ofInputStream());
+            byte[] bodyAsBytes = getDeflatedBytes(response);
+            return new EthResponse() {
+
+                @Override
+                public int statusCode() {
+                    return response.statusCode();
+                }
+
+                @Override
+                public byte[] body() {
+                    return bodyAsBytes;
+                }
+
+                @Override
+                public @NotNull Map<String, List<String>> headers() {
+                    return response.headers().map();
+                }
+            };
+        } catch (HttpConnectTimeoutException e) {
+            throw new EtherScanConnectionTimeoutException(
+                    "Connection Timeout: Could not establish connection to Etherscan server for "
+                            + httpClient.connectTimeout().orElse(DEFAULT_CONNECT_TIMEOUT) + " millis",
+                    e);
+        } catch (IOException e) {
+            throw new EtherScanConnectionException("Etherscan HTTP server network error occurred: " + e.getMessage(), e);
+        } catch (InterruptedException e) {
+            throw new EtherScanConnectionException("Etherscan HTTP server interrupt exception occurred: " + e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public EthResponse post(@NotNull URI uri, byte[] body) {
+        return urlEthHttpClient.post(uri, body);
+    }
+
+    // content-length somehow is not working properly and can't force user to override
+    // "jdk.httpclient.allowRestrictedHeaders" prop, so will force use UrlEthHttpClient
+    private EthResponse postJdk(@NotNull URI uri, byte[] body) {
+        HttpRequest.Builder requestBuilder = HttpRequest.newBuilder()
+                .POST(HttpRequest.BodyPublishers.ofByteArray(body))
+                .uri(uri)
+                .timeout(requestTimeout);
+
+        headers.forEach(requestBuilder::header);
+        requestBuilder.header("Content-Type", "application/json; charset=UTF-8");
+
+        try {
+            HttpResponse<InputStream> response = httpClient.send(requestBuilder.build(),
+                    HttpResponse.BodyHandlers.ofInputStream());
+            byte[] bodyAsBytes = getDeflatedBytes(response);
+            return new EthResponse() {
+
+                @Override
+                public int statusCode() {
+                    return response.statusCode();
+                }
+
+                @Override
+                public byte[] body() {
+                    return bodyAsBytes;
+                }
+
+                @Override
+                public @NotNull Map<String, List<String>> headers() {
+                    return response.headers().map();
+                }
+            };
+        } catch (HttpConnectTimeoutException e) {
+            throw new EtherScanConnectionTimeoutException(
+                    "Connection Timeout: Could not establish connection to Etherscan server for "
+                            + httpClient.connectTimeout().orElse(DEFAULT_CONNECT_TIMEOUT) + " millis",
+                    e);
+        } catch (IOException e) {
+            throw new EtherScanConnectionException("Etherscan HTTP server network error occurred: " + e.getMessage(), e);
+        } catch (InterruptedException e) {
+            throw new EtherScanConnectionException("Etherscan HTTP server interrupt exception occurred: " + e.getMessage(), e);
+        }
+    }
+
+    private static byte[] getDeflatedBytes(HttpResponse<InputStream> response) {
+        try {
+            Optional<String> encoding = response.headers().firstValue("content-encoding");
+            if (encoding.isEmpty()) {
+                try (var is = response.body()) {
+                    return is.readAllBytes();
+                }
+            }
+
+            switch (encoding.get().strip().toLowerCase(Locale.ROOT)) {
+                case "gzip":
+                    return new GZIPInputStream(response.body()).readAllBytes();
+                case "deflate":
+                    return new InflaterInputStream(response.body()).readAllBytes();
+                default:
+                    try (var is = response.body()) {
+                        return is.readAllBytes();
+                    }
+            }
+        } catch (IOException e) {
+            throw new UncheckedIOException(e);
+        }
+    }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/http/impl/UrlEthHttpClient.java b/src/main/java/io/goodforgod/api/etherscan/http/impl/UrlEthHttpClient.java
new file mode 100644
index 0000000..58f4658
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/http/impl/UrlEthHttpClient.java
@@ -0,0 +1,185 @@
+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.EtherScanConnectionTimeoutException;
+import io.goodforgod.api.etherscan.http.EthHttpClient;
+import io.goodforgod.api.etherscan.http.EthResponse;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.*;
+import java.time.Duration;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.InflaterInputStream;
+import org.jetbrains.annotations.ApiStatus.Internal;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Http client implementation
+ *
+ * @author GoodforGod
+ * @see EthHttpClient
+ * @since 28.10.2018
+ */
+@Internal
+public class UrlEthHttpClient implements EthHttpClient {
+
+    private static final Map<String, String> DEFAULT_HEADERS = new HashMap<>();
+
+    private static final Duration DEFAULT_CONNECT_TIMEOUT = Duration.ofSeconds(8);
+    private static final Duration DEFAULT_READ_TIMEOUT = Duration.ofMinutes(2);
+
+    static {
+        DEFAULT_HEADERS.put("Accept-Language", "en");
+        DEFAULT_HEADERS.put("Accept-Encoding", "deflate, gzip");
+        DEFAULT_HEADERS.put("User-Agent", "Chrome/68.0.3440.106");
+        DEFAULT_HEADERS.put("Accept-Charset", "UTF-8");
+    }
+
+    @Nullable
+    private final Proxy proxy;
+    private final Map<String, String> headers;
+    private final int connectTimeout;
+    private final int readTimeout;
+
+    public UrlEthHttpClient() {
+        this(DEFAULT_CONNECT_TIMEOUT);
+    }
+
+    public UrlEthHttpClient(Duration connectTimeout) {
+        this(connectTimeout, DEFAULT_READ_TIMEOUT, DEFAULT_HEADERS, null);
+    }
+
+    public UrlEthHttpClient(Duration connectTimeout, Duration readTimeout) {
+        this(connectTimeout, readTimeout, DEFAULT_HEADERS, null);
+    }
+
+    public UrlEthHttpClient(Duration connectTimeout, Duration readTimeout, Proxy proxy) {
+        this(connectTimeout, readTimeout, DEFAULT_HEADERS, null);
+    }
+
+    public UrlEthHttpClient(Duration connectTimeout, Duration readTimeout, Map<String, String> headers) {
+        this(connectTimeout, readTimeout, headers, null);
+    }
+
+    /**
+     * @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<String, String> headers,
+                            @Nullable Proxy proxy) {
+        this.connectTimeout = Math.toIntExact(connectTimeout.toMillis());
+        this.readTimeout = Math.toIntExact(readTimeout.toMillis());
+        this.headers = Collections.unmodifiableMap(headers);
+        this.proxy = proxy;
+    }
+
+    private HttpURLConnection buildConnection(URI uri, String method) throws IOException {
+        final URL url = uri.toURL();
+        final HttpURLConnection connection = (proxy == null)
+                ? (HttpURLConnection) url.openConnection()
+                : (HttpURLConnection) url.openConnection(proxy);
+        connection.setRequestMethod(method);
+        connection.setConnectTimeout(connectTimeout);
+        connection.setReadTimeout(readTimeout);
+        headers.forEach(connection::setRequestProperty);
+        return connection;
+    }
+
+    @Override
+    public EthResponse 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);
+            EthResponse ethResponse = EthResponse.of(connection.getResponseCode(), data, connection.getHeaderFields());
+            connection.disconnect();
+            return ethResponse;
+        } catch (SocketTimeoutException e) {
+            throw new EtherScanConnectionTimeoutException(
+                    "Timeout: Could not establish connection for " + connectTimeout + " millis", e);
+        } catch (Exception e) {
+            throw new EtherScanConnectionException(e.getMessage(), e);
+        }
+    }
+
+    @Override
+    public EthResponse 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);
+            EthResponse ethResponse = EthResponse.of(connection.getResponseCode(), data, connection.getHeaderFields());
+            connection.disconnect();
+            return ethResponse;
+        } catch (SocketTimeoutException e) {
+            throw new EtherScanConnectionTimeoutException(
+                    "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..92875d0
--- /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(5045L));
+    }
+
+    /**
+     * Is available for all registered free API KEYs
+     * <a href="https://docs.etherscan.io/getting-started/viewing-api-usage-statistics">Free API KEY</a>
+     */
+    static RequestQueueManager planFree() {
+        return new SemaphoreRequestQueueManager(5, Duration.ofMillis(1045L));
+    }
+
+    static RequestQueueManager planStandard() {
+        return new SemaphoreRequestQueueManager(10, Duration.ofMillis(1045L));
+    }
+
+    static RequestQueueManager planAdvanced() {
+        return new SemaphoreRequestQueueManager(20, Duration.ofMillis(1045L));
+    }
+
+    static RequestQueueManager planProfessional() {
+        return new SemaphoreRequestQueueManager(30, Duration.ofMillis(1045L));
+    }
+
+    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 66%
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..ed81b94 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 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..44c6bd5
--- /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 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/goodforgod/api/etherscan/model/Abi.java b/src/main/java/io/goodforgod/api/etherscan/model/Abi.java
new file mode 100644
index 0000000..21b6601
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/Abi.java
@@ -0,0 +1,88 @@
+package io.goodforgod.api.etherscan.model;
+
+import io.goodforgod.api.etherscan.util.BasicUtils;
+import java.util.Objects;
+
+/**
+ * @author GoodforGod
+ * @since 31.10.2018
+ */
+public class Abi {
+
+    private final String contractAbi;
+    private final boolean isVerified;
+
+    private Abi(String contractAbi, boolean isVerified) {
+        this.contractAbi = contractAbi;
+        this.isVerified = isVerified;
+    }
+
+    public static Abi verified(String contractAbi) {
+        return new Abi(contractAbi, true);
+    }
+
+    public static Abi nonVerified() {
+        return new Abi("", false);
+    }
+
+    public boolean haveAbi() {
+        return !BasicUtils.isEmpty(contractAbi);
+    }
+
+    public String getContractAbi() {
+        return contractAbi;
+    }
+
+    public boolean isVerified() {
+        return isVerified;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (!(o instanceof Abi))
+            return false;
+        Abi abi = (Abi) o;
+        return isVerified == abi.isVerified && Objects.equals(contractAbi, abi.contractAbi);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(contractAbi, isVerified);
+    }
+
+    @Override
+    public String toString() {
+        return "Abi{" +
+                "contractAbi=" + contractAbi +
+                ", isVerified=" + isVerified +
+                '}';
+    }
+
+    public static AbiBuilder builder() {
+        return new AbiBuilder();
+    }
+
+    public static 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..1d2f743
--- /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;
+    }
+
+    // <editor-fold desc="Getters">
+    public String getAddress() {
+        return address;
+    }
+
+    public Wei getBalanceInWei() {
+        return balance;
+    }
+    // </editor-fold>
+
+    @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..e0fc376
--- /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
+ */
+public abstract class BaseTx implements Comparable<BaseTx> {
+
+    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;
+
+    // <editor-fold desc="Getter">
+    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);
+    }
+    // </editor-fold>
+
+    @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..da1184b
--- /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<Block> {
+
+    long blockNumber;
+    BigInteger blockReward;
+    String timeStamp;
+    @Expose(deserialize = false, serialize = false)
+    LocalDateTime _timeStamp;
+
+    protected Block() {}
+
+    // <editor-fold desc="Getter">
+    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;
+    }
+    // </editor-fold>
+
+    @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..f77e5d4
--- /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
+ */
+public 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..058e13b
--- /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;
+
+        protected Uncle() {}
+
+        // <editor-fold desc="Getters">
+        public String getMiner() {
+            return miner;
+        }
+
+        public BigInteger getBlockreward() {
+            return blockreward;
+        }
+
+        public int getUnclePosition() {
+            return unclePosition;
+        }
+        // </editor-fold>
+
+        @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 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<Uncle> uncles;
+    private String uncleInclusionReward;
+
+    protected BlockUncle() {
+        super();
+    }
+
+    // <editor-fold desc="Getters">
+    public boolean isEmpty() {
+        return getBlockNumber() == 0 && getBlockReward() == null
+                && getTimeStamp() == null
+                && BasicUtils.isEmpty(blockMiner);
+    }
+
+    public String getBlockMiner() {
+        return blockMiner;
+    }
+
+    public List<Uncle> getUncles() {
+        return uncles;
+    }
+
+    public String getUncleInclusionReward() {
+        return uncleInclusionReward;
+    }
+    // </editor-fold>
+
+    @Override
+    public String toString() {
+        return "UncleBlock{" +
+                "blockMiner=" + blockMiner +
+                ", uncles=" + uncles +
+                ", uncleInclusionReward=" + uncleInclusionReward +
+                '}';
+    }
+
+    public static BlockUncleBuilder builder() {
+        return new BlockUncleBuilder();
+    }
+
+    public static class BlockUncleBuilder extends Block.BlockBuilder {
+
+        private long blockNumber;
+        private BigInteger blockReward;
+        private LocalDateTime timeStamp;
+        private String blockMiner;
+        private List<Uncle> 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<Uncle> 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/ContractCreation.java b/src/main/java/io/goodforgod/api/etherscan/model/ContractCreation.java
new file mode 100644
index 0000000..41e62c5
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/ContractCreation.java
@@ -0,0 +1,86 @@
+package io.goodforgod.api.etherscan.model;
+
+import java.util.Objects;
+
+public class ContractCreation {
+
+    private String contractAddress;
+    private String contractCreator;
+    private String txHash;
+
+    protected ContractCreation() {}
+
+    public String getContractAddress() {
+        return contractAddress;
+    }
+
+    public String getContractCreator() {
+        return contractCreator;
+    }
+
+    public String getTxHash() {
+        return txHash;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        ContractCreation that = (ContractCreation) o;
+        return Objects.equals(contractAddress, that.contractAddress)
+                && Objects.equals(contractCreator, that.contractCreator)
+                && Objects.equals(txHash, that.txHash);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(contractAddress, contractCreator, txHash);
+    }
+
+    @Override
+    public String toString() {
+        return "ContractCreation{" +
+                "contractAddress=" + contractAddress +
+                ", contractCreator=" + contractCreator +
+                ", txHash=" + txHash +
+                '}';
+    }
+
+    public static ContractCreationBuilder builder() {
+        return new ContractCreationBuilder();
+    }
+
+    public static class ContractCreationBuilder {
+
+        private String contractAddress;
+        private String contractCreator;
+        private String txHash;
+
+        private ContractCreationBuilder() {}
+
+        public ContractCreationBuilder withContractAddress(String contractAddress) {
+            this.contractAddress = contractAddress;
+            return this;
+        }
+
+        public ContractCreationBuilder withContractCreator(String contractCreator) {
+            this.contractCreator = contractCreator;
+            return this;
+        }
+
+        public ContractCreationBuilder withTxHash(String txHash) {
+            this.txHash = txHash;
+            return this;
+        }
+
+        public ContractCreation build() {
+            ContractCreation contractCreation = new ContractCreation();
+            contractCreation.contractAddress = contractAddress;
+            contractCreation.contractCreator = contractCreator;
+            contractCreation.txHash = txHash;
+            return contractCreation;
+        }
+    }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/EthSupply.java b/src/main/java/io/goodforgod/api/etherscan/model/EthSupply.java
new file mode 100644
index 0000000..4dbbce7
--- /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 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..e7726f9
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java
@@ -0,0 +1,147 @@
+package io.goodforgod.api.etherscan.model;
+
+import java.math.BigDecimal;
+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 BigDecimal SafeGasPrice;
+    private BigDecimal ProposeGasPrice;
+    private BigDecimal 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<BigDecimal> 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 class GasOracleBuilder {
+
+        private Long lastBlock;
+        private Wei safeGasPrice;
+        private Wei proposeGasPrice;
+        private Wei fastGasPrice;
+        private BigDecimal suggestBaseFee;
+        private List<BigDecimal> 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<BigDecimal> 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();
+            }
+            if (this.safeGasPrice != null) {
+                gasOracle.SafeGasPrice = this.safeGasPrice.asGwei();
+            }
+            if (this.fastGasPrice != null) {
+                gasOracle.FastGasPrice = this.fastGasPrice.asGwei();
+            }
+            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..d29db31
--- /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<String> topics;
+    private String logIndex;
+    @Expose(deserialize = false, serialize = false)
+    private Long _logIndex;
+
+    protected Log() {}
+
+    // <editor-fold desc="Getters">
+    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<String> getTopics() {
+        return topics;
+    }
+
+    public Long getLogIndex() {
+        if (_logIndex == null && !BasicUtils.isEmpty(logIndex)) {
+            _logIndex = BasicUtils.parseHex(logIndex).longValue();
+        }
+        return _logIndex;
+    }
+    // </editor-fold>
+
+    @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 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<String> 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<String> 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..0baa38a
--- /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 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..f651b1f
--- /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 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 64%
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..c257654 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,26 +23,23 @@ 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
     public String toString() {
         return "TokenBalance{" +
-                "tokenContract='" + tokenContract + '\'' +
+                "tokenContract=" + tokenContract +
                 '}';
     }
 }
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Tx.java b/src/main/java/io/goodforgod/api/etherscan/model/Tx.java
new file mode 100644
index 0000000..8843bd6
--- /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() {}
+
+    // <editor-fold desc="Getters">
+    public BigInteger getValue() {
+        return value;
+    }
+
+    public boolean haveError() {
+        return !BasicUtils.isEmpty(isError) && !isError.equals("0");
+    }
+
+    public String getTxReceiptStatus() {
+        return txreceipt_status;
+    }
+    // </editor-fold>
+
+    @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 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..ed8754d
--- /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() {}
+
+    // <editor-fold desc="Getters">
+    public String getTokenID() {
+        return tokenID;
+    }
+
+    public String getTokenName() {
+        return tokenName;
+    }
+
+    public String getTokenSymbol() {
+        return tokenSymbol;
+    }
+
+    public String getTokenValue() {
+        return tokenValue;
+    }
+    // </editor-fold>
+
+    @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 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..57d70cb
--- /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() {}
+
+    // <editor-fold desc="Getters">
+    public BigInteger getValue() {
+        return value;
+    }
+
+    public String getTokenName() {
+        return tokenName;
+    }
+
+    public String getTokenSymbol() {
+        return tokenSymbol;
+    }
+
+    public String getTokenDecimal() {
+        return tokenDecimal;
+    }
+    // </editor-fold>
+
+    @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 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..64df779
--- /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() {}
+
+    // <editor-fold desc="Getters">
+    public String getTokenID() {
+        return tokenID;
+    }
+
+    public String getTokenName() {
+        return tokenName;
+    }
+
+    public String getTokenSymbol() {
+        return tokenSymbol;
+    }
+
+    public String getTokenDecimal() {
+        return tokenDecimal;
+    }
+    // </editor-fold>
+
+    @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 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..91c2b27
--- /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() {}
+
+    // <editor-fold desc="Getters">
+    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;
+    }
+    // </editor-fold>
+
+    @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 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<Wei> {
+
+    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..b138c14
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java
@@ -0,0 +1,373 @@
+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<BlockProxy> {
+
+    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<String> uncles;
+
+    private String receiptsRoot;
+    private String transactionsRoot;
+    private List<TxProxy> transactions;
+
+    protected BlockProxy() {}
+
+    // <editor-fold desc="Getters">
+    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<String> getUncles() {
+        return uncles;
+    }
+
+    public String getReceiptsRoot() {
+        return receiptsRoot;
+    }
+
+    public String getTransactionsRoot() {
+        return transactionsRoot;
+    }
+
+    public List<TxProxy> getTransactions() {
+        return transactions;
+    }
+    // </editor-fold>
+
+    @Override
+    public int compareTo(@NotNull BlockProxy o) {
+        return Long.compare(getNumber(), o.getNumber());
+    }
+
+    public static BlockProxyBuilder builder() {
+        return new BlockProxyBuilder();
+    }
+
+    public static 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<String> uncles;
+        private String receiptsRoot;
+        private String transactionsRoot;
+        private List<TxProxy> 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<String> 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<TxProxy> 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;
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        BlockProxy that = (BlockProxy) o;
+        return Objects.equals(number, that.number) && Objects.equals(_number, that._number) && Objects.equals(hash, that.hash)
+                && Objects.equals(parentHash, that.parentHash) && Objects.equals(stateRoot, that.stateRoot)
+                && Objects.equals(size, that.size) && Objects.equals(_size, that._size)
+                && Objects.equals(difficulty, that.difficulty) && Objects.equals(totalDifficulty, that.totalDifficulty)
+                && Objects.equals(timestamp, that.timestamp) && Objects.equals(_timestamp, that._timestamp)
+                && Objects.equals(miner, that.miner) && Objects.equals(nonce, that.nonce)
+                && Objects.equals(extraData, that.extraData) && Objects.equals(logsBloom, that.logsBloom)
+                && Objects.equals(mixHash, that.mixHash) && Objects.equals(gasUsed, that.gasUsed)
+                && Objects.equals(_gasUsed, that._gasUsed) && Objects.equals(gasLimit, that.gasLimit)
+                && Objects.equals(_gasLimit, that._gasLimit) && Objects.equals(sha3Uncles, that.sha3Uncles)
+                && Objects.equals(uncles, that.uncles) && Objects.equals(receiptsRoot, that.receiptsRoot)
+                && Objects.equals(transactionsRoot, that.transactionsRoot) && Objects.equals(transactions, that.transactions);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(number, number, hash, parentHash, stateRoot, size, size, difficulty, totalDifficulty, timestamp,
+                timestamp, miner, nonce, extraData, logsBloom, mixHash, gasUsed, gasUsed, gasLimit, gasLimit, sha3Uncles, uncles,
+                receiptsRoot, transactionsRoot, transactions);
+    }
+
+    @Override
+    public String toString() {
+        return "BlockProxy{" +
+                "number=" + number +
+                ", number=" + _number +
+                ", hash=" + hash +
+                ", parentHash=" + parentHash +
+                ", stateRoot=" + stateRoot +
+                ", size=" + size +
+                ", size=" + _size +
+                ", difficulty=" + difficulty +
+                ", totalDifficulty=" + totalDifficulty +
+                ", timestamp=" + timestamp +
+                ", timestamp=" + _timestamp +
+                ", miner=" + miner +
+                ", nonce=" + nonce +
+                ", extraData=" + extraData +
+                ", logsBloom=" + logsBloom +
+                ", mixHash=" + mixHash +
+                ", gasUsed=" + gasUsed +
+                ", gasUsed=" + _gasUsed +
+                ", gasLimit=" + gasLimit +
+                ", gasLimit=" + _gasLimit +
+                ", sha3Uncles=" + sha3Uncles +
+                ", uncles=" + uncles +
+                ", receiptsRoot=" + receiptsRoot +
+                ", transactionsRoot=" + transactionsRoot +
+                ", transactions=" + transactions +
+                '}';
+    }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java
new file mode 100644
index 0000000..6c933c5
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java
@@ -0,0 +1,248 @@
+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<Log> logs;
+    private String logsBloom;
+
+    protected ReceiptProxy() {}
+
+    // <editor-fold desc="Getters">
+    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<Log> getLogs() {
+        return logs;
+    }
+
+    public String getLogsBloom() {
+        return logsBloom;
+    }
+    // </editor-fold>
+
+    public static ReceiptProxyBuilder builder() {
+        return new ReceiptProxyBuilder();
+    }
+
+    public static 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<Log> 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<Log> 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;
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        ReceiptProxy that = (ReceiptProxy) o;
+        return Objects.equals(root, that.root) && Objects.equals(from, that.from) && Objects.equals(to, that.to)
+                && Objects.equals(blockNumber, that.blockNumber) && Objects.equals(_blockNumber, that._blockNumber)
+                && Objects.equals(blockHash, that.blockHash) && Objects.equals(transactionHash, that.transactionHash)
+                && Objects.equals(transactionIndex, that.transactionIndex)
+                && Objects.equals(_transactionIndex, that._transactionIndex) && Objects.equals(gasUsed, that.gasUsed)
+                && Objects.equals(_gasUsed, that._gasUsed) && Objects.equals(cumulativeGasUsed, that.cumulativeGasUsed)
+                && Objects.equals(_cumulativeGasUsed, that._cumulativeGasUsed)
+                && Objects.equals(contractAddress, that.contractAddress) && Objects.equals(logs, that.logs)
+                && Objects.equals(logsBloom, that.logsBloom);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(root, from, to, blockNumber, blockNumber, blockHash, transactionHash, transactionIndex,
+                transactionIndex, gasUsed, gasUsed, cumulativeGasUsed, cumulativeGasUsed, contractAddress, logs, logsBloom);
+    }
+
+    @Override
+    public String toString() {
+        return "ReceiptProxy{" +
+                "root=" + root + '\'' +
+                ", from=" + from + '\'' +
+                ", to=" + to + '\'' +
+                ", blockNumber=" + blockNumber + '\'' +
+                ", blockNumber=" + _blockNumber +
+                ", blockHash=" + blockHash + '\'' +
+                ", transactionHash=" + transactionHash + '\'' +
+                ", transactionIndex=" + transactionIndex + '\'' +
+                ", transactionIndex=" + _transactionIndex +
+                ", gasUsed=" + gasUsed + '\'' +
+                ", gasUsed=" + _gasUsed +
+                ", cumulativeGasUsed=" + cumulativeGasUsed + '\'' +
+                ", cumulativeGasUsed=" + _cumulativeGasUsed +
+                ", contractAddress=" + contractAddress + '\'' +
+                ", logs=" + logs +
+                ", logsBloom=" + logsBloom + '\'' +
+                '}';
+    }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java
new file mode 100644
index 0000000..4372dde
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java
@@ -0,0 +1,286 @@
+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<TxProxy> {
+
+    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() {}
+
+    // <editor-fold desc="Getters">
+    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;
+    }
+    // </editor-fold>
+
+    @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 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;
+        }
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        TxProxy txProxy = (TxProxy) o;
+        return Objects.equals(to, txProxy.to) && Objects.equals(hash, txProxy.hash)
+                && Objects.equals(transactionIndex, txProxy.transactionIndex)
+                && Objects.equals(_transactionIndex, txProxy._transactionIndex) && Objects.equals(from, txProxy.from)
+                && Objects.equals(v, txProxy.v) && Objects.equals(input, txProxy.input) && Objects.equals(s, txProxy.s)
+                && Objects.equals(r, txProxy.r) && Objects.equals(nonce, txProxy.nonce) && Objects.equals(_nonce, txProxy._nonce)
+                && Objects.equals(value, txProxy.value) && Objects.equals(gas, txProxy.gas) && Objects.equals(_gas, txProxy._gas)
+                && Objects.equals(gasPrice, txProxy.gasPrice) && Objects.equals(_gasPrice, txProxy._gasPrice)
+                && Objects.equals(blockHash, txProxy.blockHash) && Objects.equals(blockNumber, txProxy.blockNumber)
+                && Objects.equals(_blockNumber, txProxy._blockNumber);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(to, hash, transactionIndex, transactionIndex, from, v, input, s, r, nonce, nonce, value, gas, gas,
+                gasPrice, gasPrice, blockHash, blockNumber, blockNumber);
+    }
+
+    @Override
+    public String toString() {
+        return "TxProxy{" +
+                "to=" + to + '\'' +
+                ", hash=" + hash + '\'' +
+                ", transactionIndex=" + transactionIndex + '\'' +
+                ", transactionIndex=" + _transactionIndex +
+                ", from=" + from + '\'' +
+                ", v=" + v + '\'' +
+                ", input=" + input + '\'' +
+                ", s=" + s + '\'' +
+                ", r=" + r + '\'' +
+                ", nonce=" + nonce + '\'' +
+                ", nonce=" + _nonce +
+                ", value=" + value + '\'' +
+                ", gas=" + gas + '\'' +
+                ", gas=" + _gas +
+                ", gasPrice=" + gasPrice + '\'' +
+                ", gasPrice=" + _gasPrice +
+                ", blockHash=" + blockHash + '\'' +
+                ", blockNumber=" + blockNumber + '\'' +
+                ", blockNumber=" + _blockNumber +
+                '}';
+    }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BaseProxyTO.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BaseProxyTO.java
new file mode 100644
index 0000000..24588c7
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BaseProxyTO.java
@@ -0,0 +1,50 @@
+package io.goodforgod.api.etherscan.model.proxy.utility;
+
+import java.util.Objects;
+
+/**
+ * @author GoodforGod
+ * @since 31.10.2018
+ */
+public abstract class BaseProxyTO {
+
+    private String id;
+    private String jsonrpc;
+    private ErrorProxyTO error;
+
+    public String getId() {
+        return id;
+    }
+
+    public String getJsonrpc() {
+        return jsonrpc;
+    }
+
+    public ErrorProxyTO getError() {
+        return error;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        BaseProxyTO that = (BaseProxyTO) o;
+        return Objects.equals(id, that.id) && Objects.equals(jsonrpc, that.jsonrpc) && Objects.equals(error, that.error);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(id, jsonrpc, error);
+    }
+
+    @Override
+    public String toString() {
+        return "BaseProxyTO{" +
+                "id=" + id +
+                ", jsonrpc=" + jsonrpc +
+                ", error=" + error +
+                '}';
+    }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BlockProxyTO.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BlockProxyTO.java
new file mode 100644
index 0000000..b6bf1c1
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BlockProxyTO.java
@@ -0,0 +1,41 @@
+package io.goodforgod.api.etherscan.model.proxy.utility;
+
+import io.goodforgod.api.etherscan.model.proxy.BlockProxy;
+import java.util.Objects;
+
+/**
+ * @author GoodforGod
+ * @since 01.11.2018
+ */
+public class BlockProxyTO extends BaseProxyTO {
+
+    private BlockProxy result;
+
+    public BlockProxy getResult() {
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        if (!super.equals(o))
+            return false;
+        BlockProxyTO that = (BlockProxyTO) o;
+        return Objects.equals(result, that.result);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), result);
+    }
+
+    @Override
+    public String toString() {
+        return "BlockProxyTO{" +
+                "result=" + result +
+                '}';
+    }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/ErrorProxyTO.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/ErrorProxyTO.java
new file mode 100644
index 0000000..5aa1f0a
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/ErrorProxyTO.java
@@ -0,0 +1,44 @@
+package io.goodforgod.api.etherscan.model.proxy.utility;
+
+import java.util.Objects;
+
+/**
+ * @author GoodforGod
+ * @since 03.11.2018
+ */
+public class ErrorProxyTO {
+
+    private String message;
+    private String code;
+
+    public String getMessage() {
+        return message;
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        ErrorProxyTO that = (ErrorProxyTO) o;
+        return Objects.equals(message, that.message) && Objects.equals(code, that.code);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(message, code);
+    }
+
+    @Override
+    public String toString() {
+        return "ErrorProxyTO{" +
+                "message=" + message +
+                ", code=" + code +
+                '}';
+    }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/StringProxyTO.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/StringProxyTO.java
new file mode 100644
index 0000000..7afbf9c
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/StringProxyTO.java
@@ -0,0 +1,40 @@
+package io.goodforgod.api.etherscan.model.proxy.utility;
+
+import java.util.Objects;
+
+/**
+ * @author GoodforGod
+ * @since 31.10.2018
+ */
+public class StringProxyTO extends BaseProxyTO {
+
+    private String result;
+
+    public String getResult() {
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        if (!super.equals(o))
+            return false;
+        StringProxyTO that = (StringProxyTO) o;
+        return Objects.equals(result, that.result);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), result);
+    }
+
+    @Override
+    public String toString() {
+        return "StringProxyTO{" +
+                "result=" + result +
+                '}';
+    }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxInfoProxyTO.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxInfoProxyTO.java
new file mode 100644
index 0000000..672585b
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxInfoProxyTO.java
@@ -0,0 +1,41 @@
+package io.goodforgod.api.etherscan.model.proxy.utility;
+
+import io.goodforgod.api.etherscan.model.proxy.ReceiptProxy;
+import java.util.Objects;
+
+/**
+ * @author GoodforGod
+ * @since 03.11.2018
+ */
+public class TxInfoProxyTO extends BaseProxyTO {
+
+    private ReceiptProxy result;
+
+    public ReceiptProxy getResult() {
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        if (!super.equals(o))
+            return false;
+        TxInfoProxyTO that = (TxInfoProxyTO) o;
+        return Objects.equals(result, that.result);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), result);
+    }
+
+    @Override
+    public String toString() {
+        return "TxInfoProxyTO{" +
+                "result=" + result +
+                '}';
+    }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxProxyTO.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxProxyTO.java
new file mode 100644
index 0000000..45b885f
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxProxyTO.java
@@ -0,0 +1,41 @@
+package io.goodforgod.api.etherscan.model.proxy.utility;
+
+import io.goodforgod.api.etherscan.model.proxy.TxProxy;
+import java.util.Objects;
+
+/**
+ * @author GoodforGod
+ * @since 01.11.2018
+ */
+public class TxProxyTO extends BaseProxyTO {
+
+    private TxProxy result;
+
+    public TxProxy getResult() {
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        if (!super.equals(o))
+            return false;
+        TxProxyTO txProxyTO = (TxProxyTO) o;
+        return Objects.equals(result, txProxyTO.result);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(super.hashCode(), result);
+    }
+
+    @Override
+    public String toString() {
+        return "TxProxyTO{" +
+                "result=" + result +
+                '}';
+    }
+}
diff --git a/src/main/java/io/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 <a href="https://etherscan.io/apis#logs">...</a>
+ *
+ * @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..f46a4e0
--- /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
+ */
+public 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..6229c76
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryImpl.java
@@ -0,0 +1,53 @@
+package io.goodforgod.api.etherscan.model.query;
+
+import io.goodforgod.api.etherscan.LogsAPI;
+import java.util.Objects;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Final builded container for The Event Log API
+ * EtherScan - API Descriptions <a href="https://etherscan.io/apis#logs">...</a>
+ *
+ * @see LogQueryBuilderImpl
+ * @see LogsAPI
+ * @author GoodforGod
+ * @since 31.10.2018
+ */
+public 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;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        LogQueryImpl logQuery = (LogQueryImpl) o;
+        return Objects.equals(params, logQuery.params);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(params);
+    }
+
+    @Override
+    public String toString() {
+        return "LogQueryImpl{" +
+                "params=" + params + '\'' +
+                '}';
+    }
+}
diff --git a/src/main/java/io/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..12fb6d0 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 {
+public 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/goodforgod/api/etherscan/model/query/LogTopicQuadro.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicQuadro.java
new file mode 100644
index 0000000..ff7bc8a
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicQuadro.java
@@ -0,0 +1,139 @@
+package io.goodforgod.api.etherscan.model.query;
+
+import static io.goodforgod.api.etherscan.model.query.LogQueryParams.*;
+
+import io.goodforgod.api.etherscan.LogsAPI;
+import io.goodforgod.api.etherscan.error.EtherScanLogQueryException;
+import java.util.Objects;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Quadro topic parameter builder for The Event Log API
+ *
+ * @see LogQueryBuilderImpl
+ * @see LogsAPI
+ * @author GoodforGod
+ * @since 31.10.2018
+ */
+public class LogTopicQuadro implements LogTopicBuilder {
+
+    private final String address;
+    private final long startBlock, endBlock;
+    private final String topic0, topic1, topic2, topic3;
+
+    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) {
+        this.address = address;
+        this.startBlock = startBlock;
+        this.endBlock = endBlock;
+        this.topic0 = topic0;
+        this.topic1 = topic1;
+        this.topic2 = topic2;
+        this.topic3 = topic3;
+    }
+
+    public LogTopicQuadro setOpTopic0_1(LogOp topic0_1_opr) {
+        this.topic0_1_opr = topic0_1_opr;
+        return this;
+    }
+
+    public LogTopicQuadro setOpTopic1_2(LogOp topic1_2_opr) {
+        this.topic1_2_opr = topic1_2_opr;
+        return this;
+    }
+
+    public LogTopicQuadro setOpTopic2_3(LogOp topic2_3_opr) {
+        this.topic2_3_opr = topic2_3_opr;
+        return this;
+    }
+
+    public LogTopicQuadro setOpTopic0_2(LogOp topic0_2_opr) {
+        this.topic0_2_opr = topic0_2_opr;
+        return this;
+    }
+
+    public LogTopicQuadro setOpTopic0_3(LogOp topic0_3_opr) {
+        this.topic0_3_opr = topic0_3_opr;
+        return this;
+    }
+
+    public LogTopicQuadro setOpTopic1_3(LogOp topic1_3_opr) {
+        this.topic1_3_opr = topic1_3_opr;
+        return this;
+    }
+
+    @Override
+    public @NotNull LogQuery build() {
+        if (topic0_1_opr == null)
+            throw new EtherScanLogQueryException("topic0_1_opr can not be null.");
+        if (topic0_2_opr == null)
+            throw new EtherScanLogQueryException("topic0_2_opr can not be null.");
+        if (topic0_3_opr == null)
+            throw new EtherScanLogQueryException("topic0_3_opr can not be null.");
+        if (topic1_2_opr == null)
+            throw new EtherScanLogQueryException("topic1_2_opr can not be null.");
+        if (topic2_3_opr == null)
+            throw new EtherScanLogQueryException("topic2_3_opr can not be null.");
+        if (topic1_3_opr == null)
+            throw new EtherScanLogQueryException("topic1_3_opr can not be null.");
+
+        return new LogQueryImpl(ADDRESS_PARAM + address
+                + FROM_BLOCK_PARAM + startBlock + TO_BLOCK_PARAM + endBlock
+                + TOPIC_0_PARAM + topic0
+                + TOPIC_1_PARAM + topic1
+                + TOPIC_2_PARAM + topic2
+                + TOPIC_3_PARAM + topic3
+                + TOPIC_0_1_OPR_PARAM + topic0_1_opr.getOperation()
+                + TOPIC_0_2_OPR_PARAM + topic0_2_opr.getOperation()
+                + TOPIC_0_3_OPR_PARAM + topic0_2_opr.getOperation()
+                + TOPIC_1_2_OPR_PARAM + topic0_2_opr.getOperation()
+                + TOPIC_1_3_OPR_PARAM + topic1_2_opr.getOperation()
+                + TOPIC_2_3_OPR_PARAM + topic0_2_opr.getOperation());
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        LogTopicQuadro that = (LogTopicQuadro) o;
+        return startBlock == that.startBlock && endBlock == that.endBlock && Objects.equals(address, that.address)
+                && Objects.equals(topic0, that.topic0) && Objects.equals(topic1, that.topic1)
+                && Objects.equals(topic2, that.topic2) && Objects.equals(topic3, that.topic3) && topic0_1_opr == that.topic0_1_opr
+                && topic1_2_opr == that.topic1_2_opr && topic2_3_opr == that.topic2_3_opr && topic0_2_opr == that.topic0_2_opr
+                && topic0_3_opr == that.topic0_3_opr && topic1_3_opr == that.topic1_3_opr;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(address, startBlock, endBlock, topic0, topic1, topic2, topic3, topic0_1_opr, topic1_2_opr,
+                topic2_3_opr, topic0_2_opr, topic0_3_opr, topic1_3_opr);
+    }
+
+    @Override
+    public String toString() {
+        return "LogTopicQuadro{" +
+                "address=" + address + '\'' +
+                ", startBlock=" + startBlock +
+                ", endBlock=" + endBlock +
+                ", topic0=" + topic0 + '\'' +
+                ", topic1=" + topic1 + '\'' +
+                ", topic2=" + topic2 + '\'' +
+                ", topic3=" + topic3 + '\'' +
+                ", topic0_1_opr=" + topic0_1_opr +
+                ", topic1_2_opr=" + topic1_2_opr +
+                ", topic2_3_opr=" + topic2_3_opr +
+                ", topic0_2_opr=" + topic0_2_opr +
+                ", topic0_3_opr=" + topic0_3_opr +
+                ", topic1_3_opr=" + topic1_3_opr +
+                '}';
+    }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicSingle.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicSingle.java
new file mode 100644
index 0000000..58d7bda
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicSingle.java
@@ -0,0 +1,64 @@
+package io.goodforgod.api.etherscan.model.query;
+
+import static io.goodforgod.api.etherscan.model.query.LogQueryParams.*;
+
+import io.goodforgod.api.etherscan.LogsAPI;
+import io.goodforgod.api.etherscan.error.EtherScanLogQueryException;
+import java.util.Objects;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Single topic parameter builder for The Event Log API
+ *
+ * @see LogQueryBuilderImpl
+ * @see LogsAPI
+ * @author GoodforGod
+ * @since 31.10.2018
+ */
+public class LogTopicSingle implements LogTopicBuilder {
+
+    private final String address;
+    private final long startBlock, endBlock;
+
+    private final String topic0;
+
+    LogTopicSingle(String address, long startBlock, long endBlock, String topic0) {
+        this.address = address;
+        this.startBlock = startBlock;
+        this.endBlock = endBlock;
+        this.topic0 = topic0;
+    }
+
+    @Override
+    public @NotNull LogQuery build() throws EtherScanLogQueryException {
+        return new LogQueryImpl(ADDRESS_PARAM + address
+                + FROM_BLOCK_PARAM + startBlock + TO_BLOCK_PARAM + endBlock
+                + TOPIC_0_PARAM + topic0);
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        LogTopicSingle that = (LogTopicSingle) o;
+        return startBlock == that.startBlock && endBlock == that.endBlock && Objects.equals(address, that.address)
+                && Objects.equals(topic0, that.topic0);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(address, startBlock, endBlock, topic0);
+    }
+
+    @Override
+    public String toString() {
+        return "LogTopicSingle{" +
+                "address=" + address + '\'' +
+                ", startBlock=" + startBlock +
+                ", endBlock=" + endBlock +
+                ", topic0=" + topic0 + '\'' +
+                '}';
+    }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTriple.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTriple.java
new file mode 100644
index 0000000..b5a25fe
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTriple.java
@@ -0,0 +1,106 @@
+package io.goodforgod.api.etherscan.model.query;
+
+import static io.goodforgod.api.etherscan.model.query.LogQueryParams.*;
+
+import io.goodforgod.api.etherscan.LogsAPI;
+import io.goodforgod.api.etherscan.error.EtherScanLogQueryException;
+import java.util.Objects;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Triple topic parameter builder for The Event Log API
+ *
+ * @see LogQueryBuilderImpl
+ * @see LogsAPI
+ * @author GoodforGod
+ * @since 31.10.2018
+ */
+public class LogTopicTriple implements LogTopicBuilder {
+
+    private final String address;
+    private final long startBlock, endBlock;
+    private final String topic0, topic1, topic2;
+
+    private LogOp topic0_1_opr, topic1_2_opr, topic0_2_opr;
+
+    LogTopicTriple(String address,
+                   long startBlock,
+                   long endBlock,
+                   String topic0,
+                   String topic1,
+                   String topic2) {
+        this.address = address;
+        this.startBlock = startBlock;
+        this.endBlock = endBlock;
+        this.topic0 = topic0;
+        this.topic1 = topic1;
+        this.topic2 = topic2;
+    }
+
+    public LogTopicTriple setOpTopic0_1(LogOp topic0_1_opr) {
+        this.topic0_1_opr = topic0_1_opr;
+        return this;
+    }
+
+    public LogTopicTriple setOpTopic0_2(LogOp topic0_2_opr) {
+        this.topic0_2_opr = topic0_2_opr;
+        return this;
+    }
+
+    public LogTopicTriple setOpTopic1_2(LogOp topic1_2_opr) {
+        this.topic1_2_opr = topic1_2_opr;
+        return this;
+    }
+
+    @Override
+    public @NotNull LogQuery build() throws EtherScanLogQueryException {
+        if (topic0_1_opr == null)
+            throw new EtherScanLogQueryException("topic0_1_opr can not be null.");
+        if (topic0_2_opr == null)
+            throw new EtherScanLogQueryException("topic0_2_opr can not be null.");
+        if (topic1_2_opr == null)
+            throw new EtherScanLogQueryException("topic1_2_opr can not be null.");
+
+        return new LogQueryImpl(ADDRESS_PARAM + address
+                + FROM_BLOCK_PARAM + startBlock + TO_BLOCK_PARAM + endBlock
+                + TOPIC_0_PARAM + topic0
+                + TOPIC_1_PARAM + topic1
+                + TOPIC_2_PARAM + topic2
+                + TOPIC_0_1_OPR_PARAM + topic0_1_opr.getOperation()
+                + TOPIC_1_2_OPR_PARAM + topic1_2_opr.getOperation()
+                + TOPIC_0_2_OPR_PARAM + topic0_2_opr.getOperation());
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        LogTopicTriple that = (LogTopicTriple) o;
+        return startBlock == that.startBlock && endBlock == that.endBlock && Objects.equals(address, that.address)
+                && Objects.equals(topic0, that.topic0) && Objects.equals(topic1, that.topic1)
+                && Objects.equals(topic2, that.topic2) && topic0_1_opr == that.topic0_1_opr && topic1_2_opr == that.topic1_2_opr
+                && topic0_2_opr == that.topic0_2_opr;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(address, startBlock, endBlock, topic0, topic1, topic2, topic0_1_opr, topic1_2_opr, topic0_2_opr);
+    }
+
+    @Override
+    public String toString() {
+        return "LogTopicTriple{" +
+                "address=" + address + '\'' +
+                ", startBlock=" + startBlock +
+                ", endBlock=" + endBlock +
+                ", topic0=" + topic0 + '\'' +
+                ", topic1=" + topic1 + '\'' +
+                ", topic2=" + topic2 + '\'' +
+                ", topic0_1_opr=" + topic0_1_opr +
+                ", topic1_2_opr=" + topic1_2_opr +
+                ", topic0_2_opr=" + topic0_2_opr +
+                '}';
+    }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTuple.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTuple.java
new file mode 100644
index 0000000..e396ace
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTuple.java
@@ -0,0 +1,83 @@
+package io.goodforgod.api.etherscan.model.query;
+
+import static io.goodforgod.api.etherscan.model.query.LogQueryParams.*;
+
+import io.goodforgod.api.etherscan.LogsAPI;
+import io.goodforgod.api.etherscan.error.EtherScanLogQueryException;
+import java.util.Objects;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Tuple topic parameter builder for The Event Log API
+ *
+ * @see LogQueryBuilderImpl
+ * @see LogsAPI
+ * @author GoodforGod
+ * @since 31.10.2018
+ */
+public class LogTopicTuple implements LogTopicBuilder {
+
+    private final String address;
+    private final long startBlock, endBlock;
+    private final String topic0, topic1;
+
+    private LogOp topic0_1_opr;
+
+    LogTopicTuple(String address,
+                  long startBlock,
+                  long endBlock,
+                  String topic0,
+                  String topic1) {
+        this.address = address;
+        this.startBlock = startBlock;
+        this.endBlock = endBlock;
+        this.topic0 = topic0;
+        this.topic1 = topic1;
+    }
+
+    public LogTopicTuple setOpTopic0_1(LogOp topic0_1_opr) {
+        this.topic0_1_opr = topic0_1_opr;
+        return this;
+    }
+
+    @Override
+    public @NotNull LogQuery build() throws EtherScanLogQueryException {
+        if (topic0_1_opr == null)
+            throw new EtherScanLogQueryException("topic0_1_opr can not be null.");
+
+        return new LogQueryImpl(ADDRESS_PARAM + address
+                + FROM_BLOCK_PARAM + startBlock + TO_BLOCK_PARAM + endBlock
+                + TOPIC_0_PARAM + topic0
+                + TOPIC_1_PARAM + topic1
+                + TOPIC_0_1_OPR_PARAM + topic0_1_opr.getOperation());
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o)
+            return true;
+        if (o == null || getClass() != o.getClass())
+            return false;
+        LogTopicTuple that = (LogTopicTuple) o;
+        return startBlock == that.startBlock && endBlock == that.endBlock && Objects.equals(address, that.address)
+                && Objects.equals(topic0, that.topic0) && Objects.equals(topic1, that.topic1)
+                && topic0_1_opr == that.topic0_1_opr;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(address, startBlock, endBlock, topic0, topic1, topic0_1_opr);
+    }
+
+    @Override
+    public String toString() {
+        return "LogTopicTuple{" +
+                "address=" + address + '\'' +
+                ", startBlock=" + startBlock +
+                ", endBlock=" + endBlock +
+                ", topic0=" + topic0 + '\'' +
+                ", topic1=" + topic1 + '\'' +
+                ", topic0_1_opr=" + topic0_1_opr +
+                '}';
+    }
+}
diff --git a/src/main/java/io/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/ContractCreationResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/ContractCreationResponseTO.java
new file mode 100644
index 0000000..e3766c3
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/response/ContractCreationResponseTO.java
@@ -0,0 +1,3 @@
+package io.goodforgod.api.etherscan.model.response;
+
+public class ContractCreationResponseTO extends BaseListResponseTO<ContractCreationTO> {}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/ContractCreationTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/ContractCreationTO.java
new file mode 100644
index 0000000..9e1551e
--- /dev/null
+++ b/src/main/java/io/goodforgod/api/etherscan/model/response/ContractCreationTO.java
@@ -0,0 +1,20 @@
+package io.goodforgod.api.etherscan.model.response;
+
+public class ContractCreationTO {
+
+    private String contractAddress;
+    private String contractCreator;
+    private String txHash;
+
+    public String getContractAddress() {
+        return contractAddress;
+    }
+
+    public String getContractCreator() {
+        return contractCreator;
+    }
+
+    public String getTxHash() {
+        return txHash;
+    }
+}
diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/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..8b01b3d
--- /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 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<TxErc1155> {
+
+}
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<TxErc20> {
+
+}
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<TxErc721> {
+
+}
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 73%
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..db98bd4 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
@@ -19,15 +19,15 @@
  */
 public 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 <T extends BaseResponseTO> 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 <T extends BaseResponseTO> void validateTxResponse(T response) {
     public static void validateAddresses(List<String> addresses) {
         for (String address : addresses) {
             if (isNotAddress(address))
-                throw new InvalidAddressException("Address [" + address + "] is not Ethereum based.");
+                throw new EtherScanInvalidAddressException("Address [" + address + "] is not Ethereum based.");
         }
     }
 
@@ -144,4 +149,8 @@ public static List<List<String>> partition(List<String> list, int pairSize) {
 
         return partitioned;
     }
+
+    public static String toAddressParam(List<String> addresses) {
+        return String.join(",", addresses);
+    }
 }
diff --git a/src/test/java/io/api/ApiRunner.java b/src/test/java/io/api/ApiRunner.java
deleted file mode 100644
index 184a84e..0000000
--- a/src/test/java/io/api/ApiRunner.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package io.api;
-
-import io.api.etherscan.core.impl.EtherScanApi;
-import io.api.etherscan.manager.impl.QueueManager;
-import io.api.etherscan.model.EthNetwork;
-import org.junit.AfterClass;
-import org.junit.Assert;
-
-public class ApiRunner extends Assert {
-
-    private static final EtherScanApi api;
-    private static final EtherScanApi apiRopsten;
-    private static final EtherScanApi apiRinkeby;
-    private static final EtherScanApi apiKovan;
-    private static final String apiKey;
-
-    static {
-        final String key = System.getenv("API_KEY");
-        apiKey = (key == null || key.isEmpty())
-                ? EtherScanApi.DEFAULT_KEY
-                : key;
-
-        final QueueManager queueManager = (EtherScanApi.DEFAULT_KEY.equals(apiKey))
-                ? QueueManager.DEFAULT_KEY_QUEUE
-                : new QueueManager(1, 1200L, 1200L, 0);
-
-        api = new EtherScanApi(ApiRunner.apiKey, EthNetwork.MAINNET, queueManager);
-        apiKovan = new EtherScanApi(ApiRunner.apiKey, EthNetwork.KOVAN, queueManager);
-        apiRopsten = new EtherScanApi(ApiRunner.apiKey, EthNetwork.ROPSTEN, queueManager);
-        apiRinkeby = new EtherScanApi(ApiRunner.apiKey, EthNetwork.RINKEBY, queueManager);
-    }
-
-    public static String getApiKey() {
-        return apiKey;
-    }
-
-    public static EtherScanApi getApi() {
-        return api;
-    }
-
-    public static EtherScanApi getApiRopsten() {
-        return apiRopsten;
-    }
-
-    public static EtherScanApi getApiRinkeby() {
-        return apiRinkeby;
-    }
-
-    public static EtherScanApi getApiKovan() {
-        return apiKovan;
-    }
-
-    @AfterClass
-    public static void cleanup() throws Exception {
-        api.close();
-        apiRopsten.close();
-        apiRinkeby.close();
-        apiKovan.close();
-    }
-}
diff --git a/src/test/java/io/api/etherscan/EtherScanApiTest.java b/src/test/java/io/api/etherscan/EtherScanApiTest.java
deleted file mode 100644
index be49435..0000000
--- a/src/test/java/io/api/etherscan/EtherScanApiTest.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package io.api.etherscan;
-
-import io.api.ApiRunner;
-import io.api.etherscan.core.impl.EtherScanApi;
-import io.api.etherscan.error.ApiException;
-import io.api.etherscan.error.ApiKeyException;
-import io.api.etherscan.error.ApiTimeoutException;
-import io.api.etherscan.executor.IHttpExecutor;
-import io.api.etherscan.executor.impl.HttpExecutor;
-import io.api.etherscan.model.Balance;
-import io.api.etherscan.model.Block;
-import io.api.etherscan.model.EthNetwork;
-import org.junit.Test;
-
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Supplier;
-
-/**
- * @author GoodforGod
- * @since 05.11.2018
- */
-public class EtherScanApiTest extends ApiRunner {
-
-    private final EthNetwork network = EthNetwork.KOVAN;
-    private final String validKey = "YourKey";
-
-    @Test
-    public void validKey() {
-        EtherScanApi api = new EtherScanApi(validKey, network);
-        assertNotNull(api);
-    }
-
-    @Test(expected = ApiKeyException.class)
-    public void emptyKey() {
-        new EtherScanApi("");
-    }
-
-    @Test(expected = ApiKeyException.class)
-    public void blankKey() {
-        new EtherScanApi("         ", network);
-    }
-
-    @Test(expected = ApiException.class)
-    public void nullNetwork() {
-        EtherScanApi api = new EtherScanApi(validKey, null);
-        assertNotNull(api);
-    }
-
-    @Test
-    public void noTimeoutOnRead() {
-        Supplier<IHttpExecutor> supplier = () -> new HttpExecutor(300);
-        EtherScanApi api = new EtherScanApi(EthNetwork.MAINNET, supplier);
-        Balance balance = api.account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7");
-        assertNotNull(balance);
-    }
-
-    @Test
-    public void noTimeoutOnReadGroli() {
-        Supplier<IHttpExecutor> supplier = () -> new HttpExecutor(300);
-        Balance balance = getApi().account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7");
-        assertNotNull(balance);
-    }
-
-    @Test
-    public void noTimeoutOnReadTobalala() {
-        Supplier<IHttpExecutor> supplier = () -> new HttpExecutor(30000);
-        Balance balance = getApi().account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7");
-        assertNotNull(balance);
-    }
-
-    @Test
-    public void noTimeoutUnlimitedAwait() {
-        Balance balance = getApi().account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7");
-        assertNotNull(balance);
-    }
-
-    @Test(expected = ApiTimeoutException.class)
-    public void timeout() throws InterruptedException {
-        TimeUnit.SECONDS.sleep(5);
-        Supplier<IHttpExecutor> supplier = () -> new HttpExecutor(300, 300);
-        EtherScanApi api = new EtherScanApi(getApiKey(), EthNetwork.KOVAN, supplier);
-        List<Block> blocks = api.account().minedBlocks("0x0010f94b296A852aAac52EA6c5Ac72e03afD032D");
-        assertNotNull(blocks);
-    }
-}
diff --git a/src/test/java/io/api/etherscan/account/AccountBalanceTest.java b/src/test/java/io/api/etherscan/account/AccountBalanceTest.java
deleted file mode 100644
index 76aca68..0000000
--- a/src/test/java/io/api/etherscan/account/AccountBalanceTest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-package io.api.etherscan.account;
-
-import io.api.ApiRunner;
-import io.api.etherscan.core.impl.EtherScanApi;
-import io.api.etherscan.error.InvalidAddressException;
-import io.api.etherscan.model.Balance;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-import java.util.Arrays;
-import java.util.Collection;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 03.11.2018
- */
-@RunWith(Parameterized.class)
-public class AccountBalanceTest extends ApiRunner {
-
-    private final EtherScanApi api;
-    private final String addressCorrect;
-    private final String addressInvalid;
-    private final String addressNoResponse;
-
-    public AccountBalanceTest(EtherScanApi api, String addressCorrect, String addressInvalid, String addressNoResponse) {
-        this.api = api;
-        this.addressCorrect = addressCorrect;
-        this.addressInvalid = addressInvalid;
-        this.addressNoResponse = addressNoResponse;
-    }
-
-    @Parameters
-    public static Collection data() {
-        return Arrays.asList(new Object[][] {
-                {
-                        getApi(),
-                        "0x8d4426f94e42f721C7116E81d6688cd935cB3b4F",
-                        "8d4426f94e42f721C7116E81d6688cd935cB3b4F",
-                        "0x1d4426f94e42f721C7116E81d6688cd935cB3b4F"
-                }
-        });
-    }
-
-    @Test
-    public void correct() {
-        Balance balance = api.account().balance(addressCorrect);
-        assertNotNull(balance);
-        assertNotNull(balance.getWei());
-        assertNotNull(balance.getMwei());
-        assertNotNull(balance.getKwei());
-        assertNotNull(balance.getGwei());
-        assertNotNull(balance.getEther());
-        assertNotNull(balance.getAddress());
-        assertNotNull(balance.toString());
-    }
-
-    @Test(expected = InvalidAddressException.class)
-    public void invalidParamWithError() {
-        Balance balance = getApi().account().balance(addressInvalid);
-    }
-
-    @Test
-    public void correctParamWithEmptyExpectedResult() {
-        Balance balance = api.account().balance(addressNoResponse);
-        assertNotNull(balance);
-        assertNotNull(balance.getWei());
-        assertNotNull(balance.getAddress());
-        assertEquals(0, balance.getWei().intValue());
-    }
-}
diff --git a/src/test/java/io/api/etherscan/account/AccountMinedBlocksTest.java b/src/test/java/io/api/etherscan/account/AccountMinedBlocksTest.java
deleted file mode 100644
index 3a46858..0000000
--- a/src/test/java/io/api/etherscan/account/AccountMinedBlocksTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package io.api.etherscan.account;
-
-import io.api.ApiRunner;
-import io.api.etherscan.core.impl.EtherScanApi;
-import io.api.etherscan.error.InvalidAddressException;
-import io.api.etherscan.model.Block;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 03.11.2018
- */
-@RunWith(Parameterized.class)
-public class AccountMinedBlocksTest extends ApiRunner {
-
-    private final EtherScanApi api;
-    private final int blocksMined;
-    private final String addressCorrect;
-    private final String addressInvalid;
-    private final String addressNoResponse;
-
-    public AccountMinedBlocksTest(EtherScanApi api,
-                                  int blocksMined,
-                                  String addressCorrect,
-                                  String addressInvalid,
-                                  String addressNoResponse) {
-        this.api = api;
-        this.blocksMined = blocksMined;
-        this.addressCorrect = addressCorrect;
-        this.addressInvalid = addressInvalid;
-        this.addressNoResponse = addressNoResponse;
-    }
-
-    @Parameters
-    public static Collection data() {
-        return Arrays.asList(new Object[][] {
-                {
-                        getApi(),
-                        223,
-                        "0xE4C6175183029A0f039bf2DFffa5C6e8F3cA9B23",
-                        "xE4C6175183029A0f039bf2DFffa5C6e8F3cA9B23",
-                        "0xE1C6175183029A0f039bf2DFffa5C6e8F3cA9B23",
-                }
-        });
-    }
-
-    @Test
-    public void correct() {
-        List<Block> blocks = api.account().minedBlocks(addressCorrect);
-        assertNotNull(blocks);
-
-        assertEquals(blocksMined, blocks.size());
-        assertBlocks(blocks);
-        assertNotNull(blocks.get(0).toString());
-
-        if (blocks.size() > 1) {
-            assertNotEquals(blocks.get(0), blocks.get(1));
-            assertNotEquals(blocks.get(0).hashCode(), blocks.get(1).hashCode());
-        }
-    }
-
-    @Test(expected = InvalidAddressException.class)
-    public void invalidParamWithError() {
-        List<Block> txs = getApi().account().minedBlocks(addressInvalid);
-    }
-
-    @Test
-    public void correctParamWithEmptyExpectedResult() {
-        List<Block> txs = api.account().minedBlocks(addressNoResponse);
-        assertNotNull(txs);
-        assertTrue(txs.isEmpty());
-    }
-
-    private void assertBlocks(List<Block> blocks) {
-        for (Block block : blocks) {
-            assertNotEquals(0, block.getBlockNumber());
-            assertNotNull(block.getBlockReward());
-            assertNotNull(block.getTimeStamp());
-        }
-    }
-}
diff --git a/src/test/java/io/api/etherscan/account/AccountTokenBalanceTest.java b/src/test/java/io/api/etherscan/account/AccountTokenBalanceTest.java
deleted file mode 100644
index 2794e95..0000000
--- a/src/test/java/io/api/etherscan/account/AccountTokenBalanceTest.java
+++ /dev/null
@@ -1,93 +0,0 @@
-package io.api.etherscan.account;
-
-import io.api.ApiRunner;
-import io.api.etherscan.core.impl.EtherScanApi;
-import io.api.etherscan.error.InvalidAddressException;
-import io.api.etherscan.model.Balance;
-import io.api.etherscan.model.TokenBalance;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-import java.util.Arrays;
-import java.util.Collection;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 03.11.2018
- */
-@RunWith(Parameterized.class)
-public class AccountTokenBalanceTest extends ApiRunner {
-
-    private final EtherScanApi api;
-    private final String contractValid;
-    private final String addressValid;
-    private final String contractInvalid;
-    private final String addressInvalid;
-    private final String addressEmpty;
-
-    public AccountTokenBalanceTest(EtherScanApi api,
-                                   String contractValid,
-                                   String addressValid,
-                                   String contractInvalid,
-                                   String addressInvalid,
-                                   String addressEmpty) {
-        this.api = api;
-        this.contractValid = contractValid;
-        this.addressValid = addressValid;
-        this.contractInvalid = contractInvalid;
-        this.addressInvalid = addressInvalid;
-        this.addressEmpty = addressEmpty;
-    }
-
-    @Parameters
-    public static Collection data() {
-        return Arrays.asList(new Object[][] {
-                {
-                        getApi(),
-                        "0x5EaC95ad5b287cF44E058dCf694419333b796123",
-                        "0x5d807e7F124EC2103a59c5249187f772c0b8D6b2",
-                        "0xEaC95ad5b287cF44E058dCf694419333b796123",
-                        "0x5807e7F124EC2103a59c5249187f772c0b8D6b2",
-                        "0x1d807e7F124EC2103a59c5249187f772c0b8D6b2",
-                }
-        });
-    }
-
-    @Test
-    public void correct() {
-        TokenBalance balance = api.account().balance(addressValid, contractValid);
-        assertNotNull(balance);
-        assertNotNull(balance.getWei());
-        assertNotNull(balance.getAddress());
-        assertNotNull(balance.getContract());
-        assertNotNull(balance.toString());
-
-        TokenBalance balance2 = new TokenBalance("125161", balance.getWei(), balance.getContract());
-        assertNotEquals(balance, balance2);
-        assertNotEquals(balance.hashCode(), balance2.hashCode());
-    }
-
-    @Test(expected = InvalidAddressException.class)
-    public void invalidAddressParamWithError() {
-        Balance balance = api.account().balance(addressInvalid, contractValid);
-    }
-
-    @Test(expected = InvalidAddressException.class)
-    public void invalidContractParamWithError() {
-        Balance balance = api.account().balance(addressValid, contractInvalid);
-    }
-
-    @Test
-    public void correctParamWithEmptyExpectedResult() {
-        TokenBalance balance = api.account().balance(addressEmpty, contractValid);
-        assertNotNull(balance);
-        assertNotNull(balance.getWei());
-        assertNotNull(balance.getAddress());
-        assertNotNull(balance.getContract());
-        assertEquals(0, balance.getWei().intValue());
-    }
-}
diff --git a/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java b/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java
deleted file mode 100644
index d1ed2bc..0000000
--- a/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package io.api.etherscan.account;
-
-import io.api.ApiRunner;
-import io.api.etherscan.core.impl.EtherScanApi;
-import io.api.etherscan.error.InvalidTxHashException;
-import io.api.etherscan.model.TxInternal;
-import io.api.etherscan.util.BasicUtils;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 03.11.2018
- */
-@RunWith(Parameterized.class)
-public class AccountTxInternalByHashTest extends ApiRunner {
-
-    private final EtherScanApi api;
-    private final int txAmount;
-    private final String validTx;
-    private final String invalidTx;
-    private final String emptyTx;
-
-    public AccountTxInternalByHashTest(EtherScanApi api, int txAmount, String validTx, String invalidTx, String emptyTx) {
-        this.api = api;
-        this.txAmount = txAmount;
-        this.validTx = validTx;
-        this.invalidTx = invalidTx;
-        this.emptyTx = emptyTx;
-    }
-
-    @Parameters
-    public static Collection data() {
-        return Arrays.asList(new Object[][] {
-                {
-                        getApi(),
-                        1,
-                        "0x1b513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b",
-                        "0xb513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b",
-                        "0x2b513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b",
-                }
-        });
-    }
-
-    @Test
-    public void correct() {
-        List<TxInternal> txs = api.account().txsInternalByHash(validTx);
-        assertNotNull(txs);
-        assertEquals(txAmount, txs.size());
-        assertTxs(txs);
-        assertNotNull(txs.get(0).getFrom());
-        assertNotNull(txs.get(0).getTimeStamp());
-        assertNotNull(txs.get(0).getGas());
-        assertNotNull(txs.get(0).getValue());
-        assertNotNull(txs.get(0).getType());
-        assertFalse(txs.get(0).haveError());
-        assertFalse(txs.get(0).haveError());
-        assertNotEquals(-1, txs.get(0).getTraceId());
-        assertTrue(BasicUtils.isEmpty(txs.get(0).getErrCode()));
-        assertNotNull(txs.get(0).toString());
-
-        if (txs.size() > 9) {
-            assertNotEquals(txs.get(0), txs.get(9));
-            assertNotEquals(txs.get(0).hashCode(), txs.get(9).hashCode());
-        }
-    }
-
-    @Test(expected = InvalidTxHashException.class)
-    public void invalidParamWithError() {
-        List<TxInternal> txs = api.account().txsInternalByHash(invalidTx);
-    }
-
-    @Test
-    public void correctParamWithEmptyExpectedResult() {
-        List<TxInternal> txs = api.account().txsInternalByHash(emptyTx);
-        assertNotNull(txs);
-        assertTrue(txs.isEmpty());
-    }
-
-    private void assertTxs(List<TxInternal> txs) {
-        for (TxInternal tx : txs) {
-            assertNotEquals(0, tx.getBlockNumber());
-            assertNotNull(tx.getFrom());
-            assertNotNull(tx.getTo());
-            assertNotNull(tx.getTimeStamp());
-        }
-    }
-}
diff --git a/src/test/java/io/api/etherscan/contract/ContractApiTest.java b/src/test/java/io/api/etherscan/contract/ContractApiTest.java
deleted file mode 100644
index 6b4d7d8..0000000
--- a/src/test/java/io/api/etherscan/contract/ContractApiTest.java
+++ /dev/null
@@ -1,41 +0,0 @@
-package io.api.etherscan.contract;
-
-import io.api.ApiRunner;
-import io.api.etherscan.error.InvalidAddressException;
-import io.api.etherscan.model.Abi;
-import org.junit.Test;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 03.11.2018
- */
-public class ContractApiTest extends ApiRunner {
-
-    @Test
-    public void correct() {
-        Abi abi = getApi().contract().contractAbi("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413");
-        assertNotNull(abi);
-        assertTrue(abi.isVerified());
-        assertTrue(abi.haveAbi());
-        assertNotNull(abi.getContractAbi());
-        assertNotNull(abi.toString());
-
-        Abi empty = Abi.verified("asg");
-        assertNotEquals(empty, abi);
-        assertNotEquals(empty.hashCode(), abi.hashCode());
-    }
-
-    @Test(expected = InvalidAddressException.class)
-    public void invalidParamWithError() {
-        getApi().contract().contractAbi("0xBBbc244D798123fDe783fCc1C72d3Bb8C189413");
-    }
-
-    @Test
-    public void correctParamWithEmptyExpectedResult() {
-        Abi abi = getApi().contract().contractAbi("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413");
-        assertNotNull(abi);
-        assertTrue(abi.isVerified());
-    }
-}
diff --git a/src/test/java/io/api/etherscan/logs/LogQueryBuilderTest.java b/src/test/java/io/api/etherscan/logs/LogQueryBuilderTest.java
deleted file mode 100644
index 85b35e8..0000000
--- a/src/test/java/io/api/etherscan/logs/LogQueryBuilderTest.java
+++ /dev/null
@@ -1,355 +0,0 @@
-package io.api.etherscan.logs;
-
-import io.api.ApiRunner;
-import io.api.etherscan.error.InvalidAddressException;
-import io.api.etherscan.error.LogQueryException;
-import io.api.etherscan.model.query.LogOp;
-import io.api.etherscan.model.query.impl.LogQuery;
-import io.api.etherscan.model.query.impl.LogQueryBuilder;
-import io.api.etherscan.model.query.impl.LogTopicQuadro;
-import org.junit.Test;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 03.11.2018
- */
-public class LogQueryBuilderTest extends ApiRunner {
-
-    @Test
-    public void singleCorrect() {
-        LogQuery single = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c")
-                .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545")
-                .build();
-
-        assertNotNull(single);
-        assertNotNull(single.getParams());
-    }
-
-    @Test(expected = InvalidAddressException.class)
-    public void singleInCorrectAddress() {
-        LogQuery single = LogQueryBuilder.with("033990122638b9132ca29c723bdf037f1a891a70c")
-                .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545")
-                .build();
-
-        assertNotNull(single);
-        assertNotNull(single.getParams());
-    }
-
-    @Test(expected = LogQueryException.class)
-    public void singleInCorrectTopic() {
-        LogQuery single = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c")
-                .topic("6516=")
-                .build();
-
-        assertNotNull(single);
-        assertNotNull(single.getParams());
-    }
-
-    @Test
-    public void tupleCorrect() {
-        LogQuery tuple = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224)
-                .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
-                        "0x72657075746174696f6e00000000000000000000000000000000000000000000")
-                .setOpTopic0_1(LogOp.AND)
-                .build();
-
-        assertNotNull(tuple);
-        assertNotNull(tuple.getParams());
-    }
-
-    @Test(expected = LogQueryException.class)
-    public void tupleInCorrectOp() {
-        LogQuery tuple = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224)
-                .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
-                        "0x72657075746174696f6e00000000000000000000000000000000000000000000")
-                .setOpTopic0_1(null)
-                .build();
-
-        assertNotNull(tuple);
-        assertNotNull(tuple.getParams());
-    }
-
-    @Test
-    public void tripleCorrect() {
-        LogQuery triple = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000)
-                .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
-                        "0x72657075746174696f6e00000000000000000000000000000000000000000000",
-                        "0x72657075746174696f6e00000000000000000000000000000000000000000000")
-                .setOpTopic0_1(LogOp.AND)
-                .setOpTopic0_2(LogOp.OR)
-                .setOpTopic1_2(LogOp.AND)
-                .build();
-
-        assertNotNull(triple);
-        assertNotNull(triple.getParams());
-    }
-
-    @Test(expected = LogQueryException.class)
-    public void tripleInCorrectOp() {
-        LogQuery triple = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000)
-                .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
-                        "0x72657075746174696f6e00000000000000000000000000000000000000000000",
-                        "0x72657075746174696f6e00000000000000000000000000000000000000000000")
-                .setOpTopic0_1(LogOp.AND)
-                .setOpTopic0_2(null)
-                .setOpTopic1_2(LogOp.AND)
-                .build();
-
-        assertNotNull(triple);
-        assertNotNull(triple.getParams());
-    }
-
-    @Test(expected = LogQueryException.class)
-    public void tripleInCorrectTopic1() {
-        LogQuery triple = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000)
-                .topic(null,
-                        "0x72657075746174696f6e00000000000000000000000000000000000000000000",
-                        "0x72657075746174696f6e00000000000000000000000000000000000000000000")
-                .setOpTopic0_1(LogOp.AND)
-                .setOpTopic0_2(LogOp.AND)
-                .setOpTopic1_2(LogOp.AND)
-                .build();
-
-        assertNotNull(triple);
-        assertNotNull(triple.getParams());
-    }
-
-    @Test(expected = LogQueryException.class)
-    public void tripleInCorrectTopic2() {
-        LogQuery triple = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000)
-                .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
-                        null,
-                        "0x72657075746174696f6e00000000000000000000000000000000000000000000")
-                .setOpTopic0_1(LogOp.AND)
-                .setOpTopic0_2(LogOp.AND)
-                .setOpTopic1_2(LogOp.AND)
-                .build();
-
-        assertNotNull(triple);
-        assertNotNull(triple.getParams());
-    }
-
-    @Test(expected = LogQueryException.class)
-    public void tripleInCorrectTopic3() {
-        LogQuery triple = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000)
-                .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
-                        "0x72657075746174696f6e00000000000000000000000000000000000000000000",
-                        null)
-                .setOpTopic0_1(LogOp.AND)
-                .setOpTopic0_2(LogOp.AND)
-                .setOpTopic1_2(LogOp.AND)
-                .build();
-
-        assertNotNull(triple);
-        assertNotNull(triple.getParams());
-    }
-
-    @Test
-    public void quadroCorrect() {
-        LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c")
-                .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
-                        "0x72657075746174696f6e00000000000000000000000000000000000000000000",
-                        "0x72657075746174696f6e00000000000000000000000000000000000000000000",
-                        "0x72657075746174696f6e00000000000000000000000000000000000000000000")
-                .setOpTopic0_1(LogOp.AND)
-                .setOpTopic0_2(LogOp.OR)
-                .setOpTopic0_3(LogOp.AND)
-                .setOpTopic1_2(LogOp.OR)
-                .setOpTopic1_3(LogOp.OR)
-                .setOpTopic2_3(LogOp.OR)
-                .build();
-
-        assertNotNull(quadro);
-        assertNotNull(quadro.getParams());
-    }
-
-    @Test(expected = LogQueryException.class)
-    public void quadroIncorrectTopic2() {
-        LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c")
-                .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
-                        null,
-                        "0x72657075746174696f6e00000000000000000000000000000000000000000000",
-                        "0x72657075746174696f6e00000000000000000000000000000000000000000000")
-                .setOpTopic0_1(LogOp.AND)
-                .setOpTopic0_2(LogOp.OR)
-                .setOpTopic0_3(LogOp.AND)
-                .setOpTopic1_2(LogOp.OR)
-                .setOpTopic1_3(LogOp.OR)
-                .setOpTopic2_3(LogOp.OR)
-                .build();
-
-        assertNotNull(quadro);
-        assertNotNull(quadro.getParams());
-    }
-
-    @Test(expected = LogQueryException.class)
-    public void tupleIncorrectTopic2() {
-        LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c")
-                .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
-                        null)
-                .setOpTopic0_1(LogOp.AND)
-                .build();
-
-        assertNotNull(quadro);
-        assertNotNull(quadro.getParams());
-    }
-
-    @Test(expected = LogQueryException.class)
-    public void tupleIncorrectTopic1() {
-        LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c")
-                .topic(null,
-                        "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545")
-                .setOpTopic0_1(LogOp.AND)
-                .build();
-
-        assertNotNull(quadro);
-        assertNotNull(quadro.getParams());
-    }
-
-    @Test(expected = LogQueryException.class)
-    public void quadroIncorrectOp1() {
-        LogTopicQuadro topicQuadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c")
-                .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
-                        "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
-                        "0x72657075746174696f6e00000000000000000000000000000000000000000000",
-                        "0x72657075746174696f6e00000000000000000000000000000000000000000000");
-
-        topicQuadro
-                .setOpTopic0_1(null)
-                .setOpTopic0_2(LogOp.OR)
-                .setOpTopic0_3(LogOp.AND)
-                .setOpTopic1_2(LogOp.OR)
-                .setOpTopic1_3(LogOp.OR)
-                .setOpTopic2_3(LogOp.OR)
-                .build();
-    }
-
-    @Test(expected = LogQueryException.class)
-    public void quadroIncorrectOp2() {
-        LogTopicQuadro topicQuadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c")
-                .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
-                        "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
-                        "0x72657075746174696f6e00000000000000000000000000000000000000000000",
-                        "0x72657075746174696f6e00000000000000000000000000000000000000000000");
-
-        topicQuadro.setOpTopic0_1(LogOp.AND)
-                .setOpTopic0_2(null)
-                .setOpTopic0_3(LogOp.AND)
-                .setOpTopic1_2(LogOp.OR)
-                .setOpTopic1_3(LogOp.OR)
-                .setOpTopic2_3(LogOp.OR)
-                .build();
-    }
-
-    @Test(expected = LogQueryException.class)
-    public void quadroIncorrectOp3() {
-        LogTopicQuadro topicQuadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c")
-                .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
-                        "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
-                        "0x72657075746174696f6e00000000000000000000000000000000000000000000",
-                        "0x72657075746174696f6e00000000000000000000000000000000000000000000");
-
-        topicQuadro
-                .setOpTopic0_1(LogOp.AND)
-                .setOpTopic0_2(LogOp.OR)
-                .setOpTopic0_3(null)
-                .setOpTopic1_2(LogOp.OR)
-                .setOpTopic1_3(LogOp.OR)
-                .setOpTopic2_3(LogOp.OR)
-                .build();
-    }
-
-    @Test(expected = LogQueryException.class)
-    public void quadroInCorrectAgainTopic() {
-        LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c")
-                .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
-                        "0x72657075746174696f6e00000000000000000000000000000000000000000000",
-                        "0x72657075746174696f6e00000000000000000000000000000000000000000000",
-                        null)
-                .setOpTopic0_1(LogOp.AND)
-                .setOpTopic0_2(LogOp.OR)
-                .setOpTopic0_3(LogOp.AND)
-                .setOpTopic1_2(LogOp.OR)
-                .setOpTopic1_3(LogOp.OR)
-                .setOpTopic2_3(LogOp.OR)
-                .build();
-
-        assertNotNull(quadro);
-        assertNotNull(quadro.getParams());
-    }
-
-    @Test(expected = LogQueryException.class)
-    public void quadroInCorrectOp4() {
-        LogTopicQuadro topicQuadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c")
-                .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
-                        "0x72657075746174696f6e00000000000000000000000000000000000000000000",
-                        "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
-                        "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545");
-
-        topicQuadro
-                .setOpTopic0_1(LogOp.AND)
-                .setOpTopic0_2(LogOp.OR)
-                .setOpTopic0_3(LogOp.AND)
-                .setOpTopic1_2(null)
-                .setOpTopic1_3(LogOp.OR)
-                .setOpTopic2_3(LogOp.OR)
-                .build();
-    }
-
-    @Test(expected = LogQueryException.class)
-    public void quadroInCorrectOp5() {
-        LogTopicQuadro topicQuadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c")
-                .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
-                        "0x72657075746174696f6e00000000000000000000000000000000000000000000",
-                        "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
-                        "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545");
-
-        topicQuadro
-                .setOpTopic0_1(LogOp.AND)
-                .setOpTopic0_2(LogOp.OR)
-                .setOpTopic0_3(LogOp.AND)
-                .setOpTopic1_2(LogOp.AND)
-                .setOpTopic1_3(null)
-                .setOpTopic2_3(LogOp.OR)
-                .build();
-    }
-
-    @Test(expected = LogQueryException.class)
-    public void quadroInCorrectOp6() {
-        LogTopicQuadro topicQuadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c")
-                .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
-                        "0x72657075746174696f6e00000000000000000000000000000000000000000000",
-                        "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
-                        "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545");
-
-        topicQuadro
-                .setOpTopic0_1(LogOp.AND)
-                .setOpTopic0_2(LogOp.OR)
-                .setOpTopic0_3(LogOp.AND)
-                .setOpTopic1_2(LogOp.AND)
-                .setOpTopic1_3(LogOp.OR)
-                .setOpTopic2_3(null)
-                .build();
-    }
-
-    @Test(expected = LogQueryException.class)
-    public void quadroInCorrectTopic() {
-        LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c")
-                .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
-                        "0x72657075746174696f6e00000000000000000000000000000000000000000000",
-                        "",
-                        "")
-                .setOpTopic0_1(LogOp.AND)
-                .setOpTopic0_2(LogOp.OR)
-                .setOpTopic0_3(LogOp.AND)
-                .setOpTopic1_2(LogOp.OR)
-                .setOpTopic1_3(LogOp.OR)
-                .setOpTopic2_3(LogOp.OR)
-                .build();
-
-        assertNotNull(quadro);
-        assertNotNull(quadro.getParams());
-    }
-}
diff --git a/src/test/java/io/api/etherscan/logs/LogsApiTest.java b/src/test/java/io/api/etherscan/logs/LogsApiTest.java
deleted file mode 100644
index 7143a83..0000000
--- a/src/test/java/io/api/etherscan/logs/LogsApiTest.java
+++ /dev/null
@@ -1,91 +0,0 @@
-package io.api.etherscan.logs;
-
-import io.api.ApiRunner;
-import io.api.etherscan.model.Log;
-import io.api.etherscan.model.query.LogOp;
-import io.api.etherscan.model.query.impl.LogQuery;
-import io.api.etherscan.model.query.impl.LogQueryBuilder;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.Parameterized;
-import org.junit.runners.Parameterized.Parameters;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 03.11.2018
- */
-@RunWith(Parameterized.class)
-public class LogsApiTest extends ApiRunner {
-
-    private final LogQuery query;
-    private final int logsSize;
-
-    public LogsApiTest(LogQuery query, int logsSize) {
-        this.query = query;
-        this.logsSize = logsSize;
-    }
-
-    @Parameters
-    public static Collection data() {
-        LogQuery single = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c")
-                .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545")
-                .build();
-
-        LogQuery singleInvalidAddr = LogQueryBuilder.with("0x13990122638b9132ca29c723bdf037f1a891a70c")
-                .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545")
-                .build();
-
-        LogQuery tupleAnd = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224)
-                .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
-                        "0x72657075746174696f6e00000000000000000000000000000000000000000000")
-                .setOpTopic0_1(LogOp.AND)
-                .build();
-
-        LogQuery tupleOr = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c")
-                .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
-                        "0x72657075746174696f6e00000000000000000000000000000000000000000000")
-                .setOpTopic0_1(LogOp.OR)
-                .build();
-
-        return Arrays.asList(new Object[][] {
-                { single, 423 },
-                { singleInvalidAddr, 0 },
-                { tupleAnd, 1 },
-                { tupleOr, 425 }
-        });
-    }
-
-    @Test
-    public void validateQuery() {
-        List<Log> logs = getApi().logs().logs(query);
-        assertEquals(logsSize, logs.size());
-
-        if (logsSize > 0) {
-            if (logsSize > 1) {
-                assertNotEquals(logs.get(0), logs.get(1));
-                assertNotEquals(logs.get(0).hashCode(), logs.get(1).hashCode());
-            }
-
-            assertNotNull(logs.get(0).getAddress());
-            assertNotNull(logs.get(0).getBlockNumber());
-            assertNotNull(logs.get(0).getData());
-            assertNotNull(logs.get(0).getTimeStamp());
-            assertNotNull(logs.get(0).getTransactionHash());
-            assertNotNull(logs.get(0).getTransactionIndex());
-            assertNotNull(logs.get(0).getGasUsed());
-            assertNotNull(logs.get(0).getTopics());
-            assertNotNull(logs.get(0).getLogIndex());
-            assertNotNull(logs.get(0).getGasPrice());
-            assertNotNull(logs.get(0).toString());
-
-            assertEquals(logs.get(0), logs.get(0));
-            assertEquals(logs.get(0).hashCode(), logs.get(0).hashCode());
-        }
-    }
-}
diff --git a/src/test/java/io/api/etherscan/proxy/ProxyBlockLastNoApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyBlockLastNoApiTest.java
deleted file mode 100644
index 5485391..0000000
--- a/src/test/java/io/api/etherscan/proxy/ProxyBlockLastNoApiTest.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package io.api.etherscan.proxy;
-
-import io.api.ApiRunner;
-import org.junit.Test;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 13.11.2018
- */
-public class ProxyBlockLastNoApiTest extends ApiRunner {
-
-    @Test
-    public void correct() {
-        long noLast = getApi().proxy().blockNoLast();
-        assertNotEquals(0, noLast);
-    }
-}
diff --git a/src/test/java/io/api/etherscan/proxy/ProxyCallApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyCallApiTest.java
deleted file mode 100644
index 07d26bd..0000000
--- a/src/test/java/io/api/etherscan/proxy/ProxyCallApiTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package io.api.etherscan.proxy;
-
-import io.api.ApiRunner;
-import io.api.etherscan.error.InvalidAddressException;
-import io.api.etherscan.error.InvalidDataHexException;
-import io.api.etherscan.util.BasicUtils;
-import org.junit.Test;
-
-import java.util.Optional;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 03.11.2018
- */
-public class ProxyCallApiTest extends ApiRunner {
-
-    @Test
-    public void correct() {
-        Optional<String> call = getApi().proxy().call("0xAEEF46DB4855E25702F8237E8f403FddcaF931C0",
-                "0x70a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724");
-        assertTrue(call.isPresent());
-        assertFalse(BasicUtils.isNotHex(call.get()));
-    }
-
-    @Test(expected = InvalidAddressException.class)
-    public void invalidParamWithError() {
-        Optional<String> call = getApi().proxy().call("0xEEF46DB4855E25702F8237E8f403FddcaF931C0",
-                "0x70a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724");
-    }
-
-    @Test(expected = InvalidDataHexException.class)
-    public void invalidParamNotHex() {
-        Optional<String> call = getApi().proxy().call("0xAEEF46DB4855E25702F8237E8f403FddcaF931C0",
-                "7-0a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724");
-    }
-
-    @Test
-    public void correctParamWithEmptyExpectedResult() {
-        Optional<String> call = getApi().proxy().call("0xAEEF16DB4855E25702F8237E8f403FddcaF931C0",
-                "0x70a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724");
-        assertTrue(call.isPresent());
-        assertFalse(call.get(), BasicUtils.isNotHex(call.get()));
-    }
-}
diff --git a/src/test/java/io/api/etherscan/proxy/ProxyCodeApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyCodeApiTest.java
deleted file mode 100644
index 9e4910c..0000000
--- a/src/test/java/io/api/etherscan/proxy/ProxyCodeApiTest.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package io.api.etherscan.proxy;
-
-import io.api.ApiRunner;
-import io.api.etherscan.error.InvalidAddressException;
-import io.api.etherscan.util.BasicUtils;
-import org.junit.Test;
-
-import java.util.Optional;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 03.11.2018
- */
-public class ProxyCodeApiTest extends ApiRunner {
-
-    @Test
-    public void correct() {
-        Optional<String> call = getApi().proxy().code("0xf75e354c5edc8efed9b59ee9f67a80845ade7d0c");
-        assertTrue(call.isPresent());
-        assertFalse(call.get(), BasicUtils.isNotHex(call.get()));
-    }
-
-    @Test(expected = InvalidAddressException.class)
-    public void invalidParamWithError() {
-        getApi().proxy().code("0f75e354c5edc8efed9b59ee9f67a80845ade7d0c");
-    }
-
-    @Test
-    public void correctParamWithEmptyExpectedResult() {
-        Optional<String> call = getApi().proxy().code("0xf15e354c5edc8efed9b59ee9f67a80845ade7d0c");
-        assertTrue(call.isPresent());
-        assertFalse(call.get(), BasicUtils.isNotHex(call.get()));
-    }
-}
diff --git a/src/test/java/io/api/etherscan/proxy/ProxyGasApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyGasApiTest.java
deleted file mode 100644
index 63e476c..0000000
--- a/src/test/java/io/api/etherscan/proxy/ProxyGasApiTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-package io.api.etherscan.proxy;
-
-import io.api.ApiRunner;
-import io.api.etherscan.error.InvalidDataHexException;
-import org.junit.Test;
-
-import java.math.BigInteger;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 03.11.2018
- */
-public class ProxyGasApiTest extends ApiRunner {
-
-    @Test
-    public void correctPrice() {
-        BigInteger price = getApi().proxy().gasPrice();
-        assertNotNull(price);
-        assertNotEquals(0, price.intValue());
-    }
-
-    @Test
-    public void correctEstimated() {
-        BigInteger price = getApi().proxy().gasEstimated();
-        assertNotNull(price);
-        assertNotEquals(0, price.intValue());
-    }
-
-    @Test
-    public void correctEstimatedWithData() {
-        String dataCustom = "606060405260728060106000396000f360606040526000606060405260728060106000396000f360606040526000";
-        BigInteger price = getApi().proxy().gasEstimated();
-        BigInteger priceCustom = getApi().proxy().gasEstimated(dataCustom);
-        assertNotNull(price);
-        assertNotNull(priceCustom);
-        assertNotEquals(price, priceCustom);
-    }
-
-    @Test(expected = InvalidDataHexException.class)
-    public void invalidParamWithError() {
-        String dataCustom = "280&60106000396000f360606040526000";
-        BigInteger priceCustom = getApi().proxy().gasEstimated(dataCustom);
-    }
-}
diff --git a/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java
deleted file mode 100644
index ecd7dca..0000000
--- a/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package io.api.etherscan.proxy;
-
-import io.api.ApiRunner;
-import io.api.etherscan.error.InvalidAddressException;
-import io.api.etherscan.util.BasicUtils;
-import org.junit.Test;
-
-import java.util.Optional;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 03.11.2018
- */
-public class ProxyStorageApiTest extends ApiRunner {
-
-    @Test
-    public void correct() {
-        Optional<String> call = getApi().proxy().storageAt("0x6e03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 0);
-        assertTrue(call.isPresent());
-        assertFalse(BasicUtils.isNotHex(call.get()));
-    }
-
-    @Test(expected = InvalidAddressException.class)
-    public void invalidParamWithError() {
-        getApi().proxy().storageAt("0xe03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 0);
-    }
-
-    @Test
-    public void correctParamWithEmptyExpectedResult() {
-        final Optional<String> call = getApi().proxy().storageAt("0x6e03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 10000);
-        assertFalse(call.isPresent());
-    }
-}
diff --git a/src/test/java/io/api/etherscan/proxy/ProxyTxCountApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyTxCountApiTest.java
deleted file mode 100644
index b81926f..0000000
--- a/src/test/java/io/api/etherscan/proxy/ProxyTxCountApiTest.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package io.api.etherscan.proxy;
-
-import io.api.ApiRunner;
-import io.api.etherscan.error.InvalidAddressException;
-import org.junit.Test;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 03.11.2018
- */
-public class ProxyTxCountApiTest extends ApiRunner {
-
-    @Test
-    public void correctSended() {
-        int count = getApi().proxy().txSendCount("0x2910543af39aba0cd09dbb2d50200b3e800a63d2");
-        assertNotEquals(0, count);
-    }
-
-    @Test
-    public void correctByBlockNo() {
-        int count = getApi().proxy().txCount(6137420);
-        assertNotEquals(0, count);
-    }
-
-    @Test(expected = InvalidAddressException.class)
-    public void invalidParamWithError() {
-        int count = getApi().proxy().txSendCount("0xe03d9cce9d60f3e9f2597e13cd4c54c55330cfd");
-    }
-
-    @Test
-    public void correctParamWithEmptyExpectedResultBlockNoExist() {
-        int count = getApi().proxy().txCount(99999999999L);
-        assertNotEquals(1, count);
-    }
-
-    @Test
-    public void correctParamWithEmptyExpectedResult() {
-        int count = getApi().proxy().txSendCount("0x1e03d9cce9d60f3e9f2597e13cd4c54c55330cfd");
-        assertNotEquals(1, count);
-    }
-}
diff --git a/src/test/java/io/api/etherscan/proxy/ProxyTxSendRawApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyTxSendRawApiTest.java
deleted file mode 100644
index 40e79a6..0000000
--- a/src/test/java/io/api/etherscan/proxy/ProxyTxSendRawApiTest.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package io.api.etherscan.proxy;
-
-import io.api.ApiRunner;
-import io.api.etherscan.error.EtherScanException;
-import io.api.etherscan.error.InvalidDataHexException;
-import org.junit.Test;
-
-import java.util.Optional;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 03.11.2018
- */
-// TODO contact etherscan and ask about method behavior
-public class ProxyTxSendRawApiTest extends ApiRunner {
-
-    public void correct() {
-        Optional<String> sendRaw = getApi().proxy()
-                .txSendRaw("0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1");
-        assertTrue(sendRaw.isPresent());
-    }
-
-    @Test(expected = InvalidDataHexException.class)
-    public void invalidParamWithError() {
-        Optional<String> sendRaw = getApi().proxy().txSendRaw("5151=0561");
-    }
-
-    @Test(expected = EtherScanException.class)
-    public void invalidParamEtherScanDataException() {
-        Optional<String> sendRaw = getApi().proxy().txSendRaw("0x1");
-    }
-
-    public void correctParamWithEmptyExpectedResult() {
-        Optional<String> sendRaw = getApi().proxy().txSendRaw("0x000000");
-        assertFalse(sendRaw.isPresent());
-    }
-}
diff --git a/src/test/java/io/api/etherscan/statistic/StatisticPriceApiTest.java b/src/test/java/io/api/etherscan/statistic/StatisticPriceApiTest.java
deleted file mode 100644
index e29a6b1..0000000
--- a/src/test/java/io/api/etherscan/statistic/StatisticPriceApiTest.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package io.api.etherscan.statistic;
-
-import io.api.ApiRunner;
-import io.api.etherscan.model.Price;
-import org.junit.Test;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 03.11.2018
- */
-public class StatisticPriceApiTest extends ApiRunner {
-
-    @Test
-    public void correct() {
-        Price price = getApi().stats().lastPrice();
-        assertNotNull(price);
-        assertNotNull(price.btcTimestamp());
-        assertNotNull(price.usdTimestamp());
-        assertNotEquals(0.0, price.inBtc());
-        assertNotEquals(0.0, price.inUsd());
-        assertNotNull(price.toString());
-
-        Price empty = new Price();
-        assertNotEquals(price, empty);
-        assertNotEquals(price.hashCode(), empty.hashCode());
-    }
-}
diff --git a/src/test/java/io/api/etherscan/statistic/StatisticTokenSupplyApiTest.java b/src/test/java/io/api/etherscan/statistic/StatisticTokenSupplyApiTest.java
deleted file mode 100644
index 0a84d01..0000000
--- a/src/test/java/io/api/etherscan/statistic/StatisticTokenSupplyApiTest.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package io.api.etherscan.statistic;
-
-import io.api.ApiRunner;
-import io.api.etherscan.error.InvalidAddressException;
-import org.junit.Test;
-
-import java.math.BigInteger;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 03.11.2018
- */
-public class StatisticTokenSupplyApiTest extends ApiRunner {
-
-    @Test
-    public void correct() {
-        BigInteger supply = getApi().stats().supply("0x57d90b64a1a57749b0f932f1a3395792e12e7055");
-        assertNotNull(supply);
-        assertNotEquals(BigInteger.ZERO, supply);
-    }
-
-    @Test(expected = InvalidAddressException.class)
-    public void invalidParamWithError() {
-        BigInteger supply = getApi().stats().supply("0x7d90b64a1a57749b0f932f1a3395792e12e7055");
-    }
-
-    @Test
-    public void correctParamWithEmptyExpectedResult() {
-        BigInteger supply = getApi().stats().supply("0x51d90b64a1a57749b0f932f1a3395792e12e7055");
-        assertNotNull(supply);
-        assertEquals(0, supply.intValue());
-    }
-}
diff --git a/src/test/java/io/api/etherscan/transaction/TransactionExecApiTest.java b/src/test/java/io/api/etherscan/transaction/TransactionExecApiTest.java
deleted file mode 100644
index 25320cc..0000000
--- a/src/test/java/io/api/etherscan/transaction/TransactionExecApiTest.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package io.api.etherscan.transaction;
-
-import io.api.ApiRunner;
-import io.api.etherscan.error.InvalidTxHashException;
-import io.api.etherscan.model.Status;
-import org.junit.Test;
-
-import java.util.Optional;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 03.11.2018
- */
-public class TransactionExecApiTest extends ApiRunner {
-
-    @Test
-    public void correct() {
-        Optional<Status> status = getApi().txs().execStatus("0x15f8e5ea1079d9a0bb04a4c58ae5fe7654b5b2b4463375ff7ffb490aa0032f3a");
-        assertTrue(status.isPresent());
-        assertTrue(status.get().haveError());
-        assertNotNull(status.get().getErrDescription());
-        assertNotNull(status.get().toString());
-
-        Status empty = new Status();
-        assertNotEquals(empty, status.get());
-        assertNotEquals(empty.hashCode(), status.get().hashCode());
-    }
-
-    @Test(expected = InvalidTxHashException.class)
-    public void invalidParamWithError() {
-        getApi().txs().execStatus("0xb513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b");
-    }
-
-    @Test
-    public void correctParamWithEmptyExpectedResult() {
-        Optional<Status> status = getApi().txs().execStatus("0x55f8e5ea1079d9a0bb04a4c58ae5fe7654b5b2b4463375ff7ffb490aa0032f3a");
-        assertTrue(status.isPresent());
-        assertFalse(status.get().haveError());
-    }
-}
diff --git a/src/test/java/io/api/etherscan/transaction/TransactionReceiptApiTest.java b/src/test/java/io/api/etherscan/transaction/TransactionReceiptApiTest.java
deleted file mode 100644
index a459355..0000000
--- a/src/test/java/io/api/etherscan/transaction/TransactionReceiptApiTest.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package io.api.etherscan.transaction;
-
-import io.api.ApiRunner;
-import io.api.etherscan.error.InvalidTxHashException;
-import org.junit.Test;
-
-import java.util.Optional;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 03.11.2018
- */
-public class TransactionReceiptApiTest extends ApiRunner {
-
-    @Test
-    public void correct() {
-        Optional<Boolean> status = getApi().txs()
-                .receiptStatus("0x513c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76");
-        assertTrue(status.isPresent());
-        assertTrue(status.get());
-    }
-
-    @Test(expected = InvalidTxHashException.class)
-    public void invalidParamWithError() {
-        getApi().txs().receiptStatus("0x13c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76");
-    }
-
-    @Test
-    public void correctParamWithEmptyExpectedResult() {
-        Optional<Boolean> status = getApi().txs()
-                .receiptStatus("0x113c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76");
-        assertFalse(status.isPresent());
-    }
-}
diff --git a/src/test/java/io/api/manager/QueueManagerTest.java b/src/test/java/io/api/manager/QueueManagerTest.java
deleted file mode 100644
index 74e674c..0000000
--- a/src/test/java/io/api/manager/QueueManagerTest.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package io.api.manager;
-
-import io.api.ApiRunner;
-import io.api.etherscan.manager.IQueueManager;
-import io.api.etherscan.manager.impl.FakeQueueManager;
-import io.api.etherscan.manager.impl.QueueManager;
-import org.junit.Test;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 03.11.2018
- */
-public class QueueManagerTest extends ApiRunner {
-
-    @Test
-    public void fakeManager() {
-        IQueueManager fakeManager = new FakeQueueManager();
-        fakeManager.takeTurn();
-        fakeManager.takeTurn();
-        fakeManager.takeTurn();
-        fakeManager.takeTurn();
-        fakeManager.takeTurn();
-        fakeManager.takeTurn();
-        assertNotNull(fakeManager);
-    }
-
-    @Test(timeout = 3500)
-    public void queueManager() {
-        IQueueManager queueManager = new QueueManager(1, 3);
-        queueManager.takeTurn();
-        queueManager.takeTurn();
-        assertNotNull(queueManager);
-    }
-
-    @Test(timeout = 4500)
-    public void queueManagerWithDelay() {
-        IQueueManager queueManager = new QueueManager(1, 2, 2);
-        queueManager.takeTurn();
-        queueManager.takeTurn();
-        assertNotNull(queueManager);
-    }
-
-    @Test
-    public void queueManagerTimeout() {
-        IQueueManager queueManager = new QueueManager(1, 3);
-        queueManager.takeTurn();
-        long start = System.currentTimeMillis();
-        queueManager.takeTurn();
-        long end = System.currentTimeMillis();
-        assertEquals(3, Math.round((double) (end - start) / 1000));
-    }
-}
diff --git a/src/test/java/io/api/util/BasicUtilsTests.java b/src/test/java/io/api/util/BasicUtilsTests.java
deleted file mode 100644
index c35bada..0000000
--- a/src/test/java/io/api/util/BasicUtilsTests.java
+++ /dev/null
@@ -1,103 +0,0 @@
-package io.api.util;
-
-import com.google.gson.Gson;
-import io.api.ApiRunner;
-import io.api.etherscan.error.EtherScanException;
-import io.api.etherscan.error.ParseException;
-import io.api.etherscan.model.utility.StringResponseTO;
-import org.junit.Test;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import static io.api.etherscan.util.BasicUtils.*;
-
-/**
- * ! NO DESCRIPTION !
- *
- * @author GoodforGod
- * @since 13.11.2018
- */
-public class BasicUtilsTests extends ApiRunner {
-
-    @Test(expected = EtherScanException.class)
-    public void responseValidateEmpty() {
-        String response = "{\"status\":\"0\",\"message\":\"No ether\",\"result\":\"status\"}";
-        StringResponseTO responseTO = new Gson().fromJson(response, StringResponseTO.class);
-        validateTxResponse(responseTO);
-    }
-
-    @Test
-    public void partitionEmpty() {
-        ArrayList<String> list = new ArrayList<>();
-        List<List<String>> lists = partition(list, 12);
-        assertTrue(lists.isEmpty());
-    }
-
-    @Test
-    public void partitionNullParam() {
-        List<List<String>> lists = partition(null, 12);
-        assertTrue(lists.isEmpty());
-    }
-
-    @Test
-    public void isBlankNull() {
-        boolean result = isBlank(null);
-        assertTrue(result);
-    }
-
-    @Test
-    public void isEmptyCollectionNull() {
-        List<String> list = null;
-        boolean result = isEmpty(list);
-        assertTrue(result);
-    }
-
-    @Test
-    public void isEmptyCollectionEmpty() {
-        ArrayList<Object> list = new ArrayList<>();
-        boolean result = isEmpty(list);
-        assertTrue(result);
-    }
-
-    @Test
-    public void isNotAddressNull() {
-        boolean result = isNotAddress("");
-        assertTrue(result);
-    }
-
-    @Test
-    public void isNotHexNull() {
-        boolean result = isNotHex("");
-        assertTrue(result);
-    }
-
-    @Test
-    public void isNotAddressInvalid() {
-        boolean result = isNotAddress("125125");
-        assertTrue(result);
-    }
-
-    @Test
-    public void isNotHexInvalid() {
-        boolean result = isNotHex("1215%");
-        assertTrue(result);
-    }
-
-    @Test(expected = EtherScanException.class)
-    public void isResponseStatusInvalidThrows() {
-        StringResponseTO responseTO = new StringResponseTO();
-        validateTxResponse(responseTO);
-    }
-
-    @Test(expected = EtherScanException.class)
-    public void isResponseNullThrows() {
-        StringResponseTO responseTO = null;
-        validateTxResponse(responseTO);
-    }
-
-    @Test(expected = ParseException.class)
-    public void isThrowParseException() {
-        throw new ParseException("Test", null, null);
-    }
-}
diff --git a/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java b/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java
new file mode 100644
index 0000000..23df478
--- /dev/null
+++ b/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java
@@ -0,0 +1,47 @@
+package io.goodforgod.api.etherscan;
+
+import io.goodforgod.api.etherscan.manager.RequestQueueManager;
+import io.goodforgod.api.etherscan.util.BasicUtils;
+import java.util.Map;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+
+public class ApiRunner extends Assertions {
+
+    private static final String DEFAULT_KEY = "YourApiKeyToken";
+
+    private static final String API_KEY;
+    private static final EtherScanAPI API;
+
+    static {
+        API_KEY = System.getenv().entrySet().stream()
+                .filter(e -> e.getKey().startsWith("ETHERSCAN_API_KEY"))
+                .filter(e -> !BasicUtils.isBlank(e.getValue()))
+                .map(Map.Entry::getValue)
+                .findFirst()
+                .orElse(DEFAULT_KEY);
+
+        final RequestQueueManager queueManager = (DEFAULT_KEY.equals(API_KEY))
+                ? RequestQueueManager.anonymous()
+                : RequestQueueManager.planFree();
+
+        API = EtherScanAPI.builder(ApiRunner.API_KEY)
+                .withNetwork(EthNetworks.MAINNET)
+                .withQueue(queueManager)
+                .withRetryOnRateLimit(5)
+                .build();
+    }
+
+    public static EtherScanAPI getApi() {
+        return API;
+    }
+
+    public static String getKey() {
+        return API_KEY;
+    }
+
+    @AfterAll
+    public static void cleanup() throws Exception {
+        API.close();
+    }
+}
diff --git a/src/test/java/io/goodforgod/api/etherscan/EtherScanAPITests.java b/src/test/java/io/goodforgod/api/etherscan/EtherScanAPITests.java
new file mode 100644
index 0000000..636e752
--- /dev/null
+++ b/src/test/java/io/goodforgod/api/etherscan/EtherScanAPITests.java
@@ -0,0 +1,51 @@
+package io.goodforgod.api.etherscan;
+
+import io.goodforgod.api.etherscan.error.EtherScanConnectionException;
+import io.goodforgod.api.etherscan.error.EtherScanKeyException;
+import io.goodforgod.api.etherscan.http.EthHttpClient;
+import io.goodforgod.api.etherscan.http.impl.UrlEthHttpClient;
+import java.net.URI;
+import java.time.Duration;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Supplier;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author GoodforGod
+ * @since 05.11.2018
+ */
+class EtherScanAPITests extends ApiRunner {
+
+    private final EthNetworks network = EthNetworks.SEPOLIA;
+
+    @Test
+    void validKey() {
+        String validKey = "YourKey";
+        EtherScanAPI api = EtherScanAPI.builder(validKey).withApiKey(validKey).withNetwork(network).build();
+        assertNotNull(api);
+    }
+
+    @Test
+    void emptyKey() {
+        assertThrows(EtherScanKeyException.class, () -> EtherScanAPI.builder("someKey").withApiKey("").build());
+    }
+
+    @Test
+    void blankKey() {
+        assertThrows(EtherScanKeyException.class,
+                () -> EtherScanAPI.builder("someKey").withApiKey("         ").withNetwork(network).build());
+    }
+
+    @Test
+    void timeout() throws InterruptedException {
+        TimeUnit.SECONDS.sleep(5);
+        Supplier<EthHttpClient> supplier = () -> new UrlEthHttpClient(Duration.ofMillis(300), Duration.ofMillis(300));
+        EtherScanAPI api = EtherScanAPI.builder(ApiRunner.getKey())
+                .withNetwork(() -> URI.create("https://api-unknown.etherscan.io/api"))
+                .withHttpClient(supplier)
+                .build();
+
+        assertThrows(EtherScanConnectionException.class,
+                () -> api.account().blocksMined("0x0010f94b296A852aAac52EA6c5Ac72e03afD032D"));
+    }
+}
diff --git a/src/test/java/io/api/etherscan/account/AccountBalanceListTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceListTests.java
similarity index 69%
rename from src/test/java/io/api/etherscan/account/AccountBalanceListTest.java
rename to src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceListTests.java
index fdeb1e9..0054a84 100644
--- a/src/test/java/io/api/etherscan/account/AccountBalanceListTest.java
+++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceListTests.java
@@ -1,25 +1,22 @@
-package io.api.etherscan.account;
-
-import io.api.ApiRunner;
-import io.api.etherscan.error.InvalidAddressException;
-import io.api.etherscan.model.Balance;
-import io.api.support.AddressUtil;
-import org.junit.Test;
+package io.goodforgod.api.etherscan.account;
 
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException;
+import io.goodforgod.api.etherscan.model.Balance;
+import io.goodforgod.api.etherscan.support.AddressUtil;
 import java.math.BigInteger;
 import java.util.ArrayList;
 import java.util.List;
+import org.junit.jupiter.api.Test;
 
 /**
- * ! NO DESCRIPTION !
- *
  * @author GoodforGod
  * @since 03.11.2018
  */
-public class AccountBalanceListTest extends ApiRunner {
+class AccountBalanceListTests extends ApiRunner {
 
     @Test
-    public void correct() {
+    void correct() {
         List<String> addresses = new ArrayList<>();
         addresses.add("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9");
         addresses.add("0xC9F32CE1127e44C51cbD182D6364F3D707Fd0d47");
@@ -32,19 +29,15 @@ public void correct() {
         assertNotEquals(balances.get(0).hashCode(), balances.get(1).hashCode());
         for (Balance balance : balances) {
             assertNotNull(balance.getAddress());
-            assertNotNull(balance.getGwei());
-            assertNotNull(balance.getKwei());
-            assertNotNull(balance.getMwei());
-            assertNotNull(balance.getEther());
-            assertNotNull(balance.getGwei());
+            assertNotNull(balance.getBalanceInWei());
             assertNotNull(balance.getAddress());
-            assertNotEquals(BigInteger.ZERO, balance.getWei());
+            assertNotEquals(BigInteger.ZERO, balance.getBalanceInWei().asWei());
             assertNotNull(balance.toString());
         }
     }
 
     @Test
-    public void correctMoreThat20Addresses() {
+    void correctMoreThat20Addresses() {
         List<String> addresses = AddressUtil.genRealAddresses();
 
         List<Balance> balances = getApi().account().balances(addresses);
@@ -58,17 +51,17 @@ public void correctMoreThat20Addresses() {
         assertNotEquals(balances.get(0), balances.get(1));
     }
 
-    @Test(expected = InvalidAddressException.class)
-    public void invalidParamWithError() {
+    @Test
+    void invalidParamWithError() {
         List<String> addresses = new ArrayList<>();
         addresses.add("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9");
         addresses.add("C9F32CE1127e44C51cbD182D6364F3D707Fd0d47");
 
-        getApi().account().balances(addresses);
+        assertThrows(EtherScanInvalidAddressException.class, () -> getApi().account().balances(addresses));
     }
 
     @Test
-    public void emptyParamList() {
+    void emptyParamList() {
         List<String> addresses = new ArrayList<>();
         List<Balance> balances = getApi().account().balances(addresses);
         assertNotNull(balances);
@@ -76,7 +69,7 @@ public void emptyParamList() {
     }
 
     @Test
-    public void correctParamWithEmptyExpectedResult() {
+    void correctParamWithEmptyExpectedResult() {
         List<String> addresses = new ArrayList<>();
         addresses.add("0x1327cb34984c3992ec1EA0eAE98Ccf80A74f95B9");
         addresses.add("0xC1F32CE1127e44C51cbD182D6364F3D707Fd0d47");
@@ -87,7 +80,7 @@ public void correctParamWithEmptyExpectedResult() {
         assertEquals(2, balances.size());
         for (Balance balance : balances) {
             assertNotNull(balance.getAddress());
-            assertEquals(0, balance.getWei().intValue());
+            assertEquals(0, balance.getBalanceInWei().asWei().intValue());
         }
     }
 }
diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceTests.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceTests.java
new file mode 100644
index 0000000..ed537c6
--- /dev/null
+++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceTests.java
@@ -0,0 +1,40 @@
+package io.goodforgod.api.etherscan.account;
+
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.EtherScanAPI;
+import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException;
+import io.goodforgod.api.etherscan.model.Balance;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author GoodforGod
+ * @since 03.11.2018
+ */
+class AccountBalanceTests extends ApiRunner {
+
+    private final EtherScanAPI api = getApi();
+
+    @Test
+    void correct() {
+        Balance balance = api.account().balance("0x8d4426f94e42f721C7116E81d6688cd935cB3b4F");
+        assertNotNull(balance);
+        assertNotNull(balance.getBalanceInWei());
+        assertNotNull(balance.getAddress());
+        assertNotNull(balance.toString());
+    }
+
+    @Test
+    void invalidParamWithError() {
+        assertThrows(EtherScanInvalidAddressException.class,
+                () -> getApi().account().balance("8d4426f94e42f721C7116E81d6688cd935cB3b4F"));
+    }
+
+    @Test
+    void correctParamWithEmptyExpectedResult() {
+        Balance balance = api.account().balance("0x1d4426f94e42f721C7116E81d6688cd935cB3b4F");
+        assertNotNull(balance);
+        assertNotNull(balance.getBalanceInWei());
+        assertNotNull(balance.getAddress());
+        assertEquals(0, balance.getBalanceInWei().asWei().intValue());
+    }
+}
diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountMinedBlocksTests.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountMinedBlocksTests.java
new file mode 100644
index 0000000..3e19e96
--- /dev/null
+++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountMinedBlocksTests.java
@@ -0,0 +1,53 @@
+package io.goodforgod.api.etherscan.account;
+
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.EtherScanAPI;
+import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException;
+import io.goodforgod.api.etherscan.model.Block;
+import java.util.List;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author GoodforGod
+ * @since 03.11.2018
+ */
+class AccountMinedBlocksTests extends ApiRunner {
+
+    private final EtherScanAPI api = getApi();
+
+    @Test
+    void correct() {
+        List<Block> blocks = api.account().blocksMined("0xE4C6175183029A0f039bf2DFffa5C6e8F3cA9B23");
+        assertNotNull(blocks);
+
+        assertEquals(223, blocks.size());
+        assertBlocks(blocks);
+        assertNotNull(blocks.get(0).toString());
+
+        if (blocks.size() > 1) {
+            assertNotEquals(blocks.get(0), blocks.get(1));
+            assertNotEquals(blocks.get(0).hashCode(), blocks.get(1).hashCode());
+        }
+    }
+
+    @Test
+    void invalidParamWithError() {
+        assertThrows(EtherScanInvalidAddressException.class,
+                () -> getApi().account().blocksMined("xE4C6175183029A0f039bf2DFffa5C6e8F3cA9B23"));
+    }
+
+    @Test
+    void correctParamWithEmptyExpectedResult() {
+        List<Block> txs = api.account().blocksMined("0xE1C6175183029A0f039bf2DFffa5C6e8F3cA9B23");
+        assertNotNull(txs);
+        assertTrue(txs.isEmpty());
+    }
+
+    private void assertBlocks(List<Block> blocks) {
+        for (Block block : blocks) {
+            assertNotEquals(0, block.getBlockNumber());
+            assertNotNull(block.getBlockReward());
+            assertNotNull(block.getTimeStamp());
+        }
+    }
+}
diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountTokenBalanceTests.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTokenBalanceTests.java
new file mode 100644
index 0000000..3919982
--- /dev/null
+++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTokenBalanceTests.java
@@ -0,0 +1,56 @@
+package io.goodforgod.api.etherscan.account;
+
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.EtherScanAPI;
+import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException;
+import io.goodforgod.api.etherscan.model.TokenBalance;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author GoodforGod
+ * @since 03.11.2018
+ */
+class AccountTokenBalanceTests extends ApiRunner {
+
+    private final EtherScanAPI api = getApi();
+
+    @Test
+    void correct() {
+        TokenBalance balance = api.account().balance("0x5d807e7F124EC2103a59c5249187f772c0b8D6b2",
+                "0x5EaC95ad5b287cF44E058dCf694419333b796123");
+        assertNotNull(balance);
+        assertNotNull(balance.getBalanceInWei());
+        assertNotNull(balance.getAddress());
+        assertNotNull(balance.getContract());
+        assertNotNull(balance.toString());
+
+        TokenBalance balance2 = new TokenBalance("125161", balance.getBalanceInWei(), balance.getContract());
+        assertNotEquals(balance, balance2);
+        assertNotEquals(balance.hashCode(), balance2.hashCode());
+    }
+
+    @Test
+    void invalidAddressParamWithError() {
+        assertThrows(EtherScanInvalidAddressException.class,
+                () -> api.account().balance("0x5807e7F124EC2103a59c5249187f772c0b8D6b2",
+                        "0x5EaC95ad5b287cF44E058dCf694419333b796123"));
+    }
+
+    @Test
+    void invalidContractParamWithError() {
+        assertThrows(EtherScanInvalidAddressException.class,
+                () -> api.account().balance("0x5d807e7F124EC2103a59c5249187f772c0b8D6b2",
+                        "0xEaC95ad5b287cF44E058dCf694419333b796123"));
+    }
+
+    @Test
+    void correctParamWithEmptyExpectedResult() {
+        TokenBalance balance = api.account().balance("0x1d807e7F124EC2103a59c5249187f772c0b8D6b2",
+                "0x5EaC95ad5b287cF44E058dCf694419333b796123");
+        assertNotNull(balance);
+        assertNotNull(balance.getBalanceInWei());
+        assertNotNull(balance.getAddress());
+        assertNotNull(balance.getContract());
+        assertEquals(0, balance.getBalanceInWei().asWei().intValue());
+    }
+}
diff --git a/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxErc20Tests.java
similarity index 58%
rename from src/test/java/io/api/etherscan/account/AccountTxTokenTest.java
rename to src/test/java/io/goodforgod/api/etherscan/account/AccountTxErc20Tests.java
index b82d4d1..4239bcd 100644
--- a/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java
+++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxErc20Tests.java
@@ -1,27 +1,24 @@
-package io.api.etherscan.account;
-
-import io.api.ApiRunner;
-import io.api.etherscan.error.InvalidAddressException;
-import io.api.etherscan.model.TxToken;
-import org.junit.Test;
+package io.goodforgod.api.etherscan.account;
 
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException;
+import io.goodforgod.api.etherscan.model.TxErc20;
 import java.util.List;
+import org.junit.jupiter.api.Test;
 
 /**
- * ! NO DESCRIPTION !
- *
  * @author GoodforGod
  * @since 03.11.2018
  */
-public class AccountTxTokenTest extends ApiRunner {
+class AccountTxErc20Tests extends ApiRunner {
 
     @Test
-    public void correct() {
-        List<TxToken> txs = getApi().account().txsToken("0xE376F69ED2218076682e2b3B7b9099eC50aD68c4");
+    void correct() {
+        List<TxErc20> txs = getApi().account().txsErc20("0xE376F69ED2218076682e2b3B7b9099eC50aD68c4");
         assertNotNull(txs);
         assertEquals(3, txs.size());
         assertTxs(txs);
-        assertNotEquals(0, txs.get(0).getGasPrice());
+        assertNotEquals(0, txs.get(0).getGasPrice().asWei().intValue());
         assertNotEquals(-1, txs.get(0).getNonce());
 
         assertNotNull(txs.get(0).toString());
@@ -35,35 +32,36 @@ public void correct() {
     }
 
     @Test
-    public void correctStartBlock() {
-        List<TxToken> txs = getApi().account().txsToken("0x36ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7", 5578167);
+    void correctStartBlock() {
+        List<TxErc20> txs = getApi().account().txsErc20("0x36ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7", 5578167);
         assertNotNull(txs);
         assertEquals(11, txs.size());
         assertTxs(txs);
     }
 
     @Test
-    public void correctStartBlockEndBlock() {
-        List<TxToken> txs = getApi().account().txsToken("0x36ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7", 5578167, 5813576);
+    void correctStartBlockEndBlock() {
+        List<TxErc20> txs = getApi().account().txsErc20("0x36ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7", 5578167, 5813576);
         assertNotNull(txs);
         assertEquals(5, txs.size());
         assertTxs(txs);
     }
 
-    @Test(expected = InvalidAddressException.class)
-    public void invalidParamWithError() {
-        getApi().account().txsToken("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7");
+    @Test
+    void invalidParamWithError() {
+        assertThrows(EtherScanInvalidAddressException.class,
+                () -> getApi().account().txsErc20("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"));
     }
 
     @Test
-    public void correctParamWithEmptyExpectedResult() {
-        List<TxToken> txs = getApi().account().txsToken("0x31ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7");
+    void correctParamWithEmptyExpectedResult() {
+        List<TxErc20> txs = getApi().account().txsErc20("0x31ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7");
         assertNotNull(txs);
         assertTrue(txs.isEmpty());
     }
 
-    private void assertTxs(List<TxToken> txs) {
-        for (TxToken tx : txs) {
+    private void assertTxs(List<TxErc20> txs) {
+        for (TxErc20 tx : txs) {
             assertNotNull(tx.getBlockHash());
             assertNotNull(tx.getTokenName());
             assertNotNull(tx.getTokenSymbol());
@@ -73,7 +71,7 @@ private void assertTxs(List<TxToken> txs) {
             assertNotNull(tx.getTokenDecimal());
             assertNotEquals(-1, (tx.getConfirmations()));
             assertNotNull(tx.getGasUsed());
-            assertNotEquals(-1, tx.getCumulativeGasUsed());
+            assertNotEquals(-1, tx.getGasUsedCumulative().asWei().intValue());
             assertNotEquals(-1, tx.getTransactionIndex());
         }
     }
diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalByHashTests.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalByHashTests.java
new file mode 100644
index 0000000..eb06b60
--- /dev/null
+++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalByHashTests.java
@@ -0,0 +1,65 @@
+package io.goodforgod.api.etherscan.account;
+
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.EtherScanAPI;
+import io.goodforgod.api.etherscan.error.EtherScanInvalidTxHashException;
+import io.goodforgod.api.etherscan.model.TxInternal;
+import io.goodforgod.api.etherscan.util.BasicUtils;
+import java.util.List;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author GoodforGod
+ * @since 03.11.2018
+ */
+class AccountTxInternalByHashTests extends ApiRunner {
+
+    private final EtherScanAPI api = getApi();
+
+    @Test
+    void correct() {
+        List<TxInternal> txs = api.account()
+                .txsInternalByHash("0x1b513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b");
+        assertNotNull(txs);
+        assertEquals(1, txs.size());
+        assertTxs(txs);
+        assertNotNull(txs.get(0).getFrom());
+        assertNotNull(txs.get(0).getTimeStamp());
+        assertNotNull(txs.get(0).getGas());
+        assertNotNull(txs.get(0).getValue());
+        assertNotNull(txs.get(0).getType());
+        assertFalse(txs.get(0).haveError());
+        assertFalse(txs.get(0).haveError());
+        assertNotEquals("-1", txs.get(0).getTraceIdAsString());
+        assertTrue(BasicUtils.isEmpty(txs.get(0).getErrCode()));
+        assertNotNull(txs.get(0).toString());
+
+        if (txs.size() > 9) {
+            assertNotEquals(txs.get(0), txs.get(9));
+            assertNotEquals(txs.get(0).hashCode(), txs.get(9).hashCode());
+        }
+    }
+
+    @Test
+    void invalidParamWithError() {
+        assertThrows(EtherScanInvalidTxHashException.class,
+                () -> api.account().txsInternalByHash("0xb513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b"));
+    }
+
+    @Test
+    void correctParamWithEmptyExpectedResult() {
+        List<TxInternal> txs = api.account()
+                .txsInternalByHash("0x2b513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b");
+        assertNotNull(txs);
+        assertTrue(txs.isEmpty());
+    }
+
+    private void assertTxs(List<TxInternal> txs) {
+        for (TxInternal tx : txs) {
+            assertNotEquals(0, tx.getBlockNumber());
+            assertNotNull(tx.getFrom());
+            assertNotNull(tx.getTo());
+            assertNotNull(tx.getTimeStamp());
+        }
+    }
+}
diff --git a/src/test/java/io/api/etherscan/account/AccountTxInternalTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalTests.java
similarity index 62%
rename from src/test/java/io/api/etherscan/account/AccountTxInternalTest.java
rename to src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalTests.java
index f993c39..1d4220d 100644
--- a/src/test/java/io/api/etherscan/account/AccountTxInternalTest.java
+++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalTests.java
@@ -1,22 +1,19 @@
-package io.api.etherscan.account;
-
-import io.api.ApiRunner;
-import io.api.etherscan.error.InvalidAddressException;
-import io.api.etherscan.model.TxInternal;
-import org.junit.Test;
+package io.goodforgod.api.etherscan.account;
 
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException;
+import io.goodforgod.api.etherscan.model.TxInternal;
 import java.util.List;
+import org.junit.jupiter.api.Test;
 
 /**
- * ! NO DESCRIPTION !
- *
  * @author GoodforGod
  * @since 03.11.2018
  */
-public class AccountTxInternalTest extends ApiRunner {
+class AccountTxInternalTests extends ApiRunner {
 
     @Test
-    public void correct() {
+    void correct() {
         List<TxInternal> txs = getApi().account().txsInternal("0x2C1ba59D6F58433FB1EaEe7d20b26Ed83bDA51A3");
         assertNotNull(txs);
         assertEquals(66, txs.size());
@@ -25,28 +22,31 @@ public void correct() {
     }
 
     @Test
-    public void correctStartBlock() {
+    void correctStartBlock() {
         List<TxInternal> txs = getApi().account().txsInternal("0x2C1ba59D6F58433FB1EaEe7d20b26Ed83bDA51A3", 2558775);
         assertNotNull(txs);
         assertEquals(24, txs.size());
+        assertNotEquals(txs.get(0), txs.get(1));
+        assertNotEquals(txs.get(0).toString(), txs.get(1).toString());
         assertTxs(txs);
     }
 
     @Test
-    public void correctStartBlockEndBlock() {
+    void correctStartBlockEndBlock() {
         List<TxInternal> txs = getApi().account().txsInternal("0x2C1ba59D6F58433FB1EaEe7d20b26Ed83bDA51A3", 2558775, 2685504);
         assertNotNull(txs);
         assertEquals(21, txs.size());
         assertTxs(txs);
     }
 
-    @Test(expected = InvalidAddressException.class)
-    public void invalidParamWithError() {
-        List<TxInternal> txs = getApi().account().txsInternal("0x2C1ba59D6F58433FB1EaEe7d20b26Ed83bDA51");
+    @Test
+    void invalidParamWithError() {
+        assertThrows(EtherScanInvalidAddressException.class,
+                () -> getApi().account().txsInternal("0x2C1ba59D6F58433FB1EaEe7d20b26Ed83bDA51"));
     }
 
     @Test
-    public void correctParamWithEmptyExpectedResult() {
+    void correctParamWithEmptyExpectedResult() {
         List<TxInternal> txs = getApi().account().txsInternal("0x2C1ba59D6F58433FB2EaEe7d20b26Ed83bDA51A3");
         assertNotNull(txs);
         assertTrue(txs.isEmpty());
diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc1155TokenTests.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc1155TokenTests.java
new file mode 100644
index 0000000..d8cbb73
--- /dev/null
+++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc1155TokenTests.java
@@ -0,0 +1,81 @@
+package io.goodforgod.api.etherscan.account;
+
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException;
+import io.goodforgod.api.etherscan.model.TxErc1155;
+import java.util.List;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author GoodforGod
+ * @since 14.05.2023
+ */
+class AccountTxRc1155TokenTests extends ApiRunner {
+
+    @Test
+    void correct() {
+        List<TxErc1155> txs = getApi().account().txsErc1155("0xE4C8324534C0C6bCA174Cd0F02fAC9889C36bA59");
+        assertNotNull(txs);
+        assertFalse(txs.isEmpty());
+        assertTxs(txs);
+        assertNotEquals(0, txs.get(0).getGasPrice().asWei().intValue());
+        assertNotEquals(-1, txs.get(0).getNonce());
+
+        assertNotNull(txs.get(0).toString());
+        assertNotEquals(txs.get(0).toString(), txs.get(1).toString());
+
+        assertNotEquals(txs.get(0), txs.get(1));
+        assertNotEquals(txs.get(0).hashCode(), txs.get(1).hashCode());
+
+        assertEquals(txs.get(1), txs.get(1));
+        assertEquals(txs.get(1).hashCode(), txs.get(1).hashCode());
+    }
+
+    @Test
+    void correctStartBlock() {
+        List<TxErc1155> txs = getApi().account().txsErc1155("0xE4C8324534C0C6bCA174Cd0F02fAC9889C36bA59", 14275897);
+        assertNotNull(txs);
+        assertFalse(txs.isEmpty());
+        assertTxs(txs);
+    }
+
+    @Test
+    void correctStartBlockEndBlock() {
+        List<TxErc1155> txs = getApi().account().txsErc1155("0xE4C8324534C0C6bCA174Cd0F02fAC9889C36bA59", 14275897, 15148929);
+        assertNotNull(txs);
+        assertEquals(11, txs.size());
+        assertTxs(txs);
+    }
+
+    @Test
+    void invalidParamWithError() {
+        assertThrows(EtherScanInvalidAddressException.class,
+                () -> getApi().account().txsErc1155("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"));
+    }
+
+    @Test
+    void correctParamWithEmptyExpectedResult() {
+        List<TxErc1155> txs = getApi().account().txsErc1155("0x31ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7");
+        assertNotNull(txs);
+        assertTrue(txs.isEmpty());
+    }
+
+    private void assertTxs(List<TxErc1155> txs) {
+        txs.forEach(this::asserTx);
+    }
+
+    private void asserTx(TxErc1155 tx) {
+        assertNotNull(tx.getBlockHash());
+        assertNotNull(tx.getTokenName());
+        assertNotNull(tx.getTokenSymbol());
+        assertNotNull(tx.getFrom());
+        assertNotNull(tx.getTo());
+        assertNotNull(tx.getTimeStamp());
+        assertNotNull(tx.getTokenID());
+        assertNotNull(tx.getTokenValue());
+        assertNotEquals(-1, (tx.getConfirmations()));
+        assertNotNull(tx.getGasUsed());
+        assertNotEquals(-1, tx.getGasUsedCumulative().asWei().intValue());
+        assertNotEquals(-1, tx.getTransactionIndex());
+    }
+}
diff --git a/src/test/java/io/api/etherscan/account/AccountTxRc721TokenTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTests.java
similarity index 52%
rename from src/test/java/io/api/etherscan/account/AccountTxRc721TokenTest.java
rename to src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTests.java
index 0afa12f..6c61a4c 100644
--- a/src/test/java/io/api/etherscan/account/AccountTxRc721TokenTest.java
+++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTests.java
@@ -1,25 +1,24 @@
-package io.api.etherscan.account;
-
-import io.api.ApiRunner;
-import io.api.etherscan.error.InvalidAddressException;
-import io.api.etherscan.model.TxToken;
-import org.junit.Test;
+package io.goodforgod.api.etherscan.account;
 
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException;
+import io.goodforgod.api.etherscan.model.TxErc721;
 import java.util.List;
+import org.junit.jupiter.api.Test;
 
 /**
  * @author NGuggs
  * @since 11.28.2021
  */
-public class AccountTxRc721TokenTest extends ApiRunner {
+class AccountTxRc721TokenTests extends ApiRunner {
 
     @Test
-    public void correct() {
-        List<TxToken> txs = getApi().account().txsNftToken("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67");
+    void correct() {
+        List<TxErc721> txs = getApi().account().txsErc721("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67");
         assertNotNull(txs);
         assertEquals(16, txs.size());
         assertTxs(txs);
-        assertNotEquals(0, txs.get(0).getGasPrice());
+        assertNotEquals(0, txs.get(0).getGasPrice().asWei().intValue());
         assertNotEquals(-1, txs.get(0).getNonce());
 
         assertNotNull(txs.get(0).toString());
@@ -33,8 +32,8 @@ public void correct() {
     }
 
     @Test
-    public void correctStartBlock() {
-        List<TxToken> txs = getApi().account().txsNftToken("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67", 4762071);
+    void correctStartBlock() {
+        List<TxErc721> txs = getApi().account().txsErc721("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67", 4762071);
         System.out.println(txs);
         assertNotNull(txs);
         assertEquals(5, txs.size());
@@ -42,28 +41,29 @@ public void correctStartBlock() {
     }
 
     @Test
-    public void correctStartBlockEndBlock() {
-        List<TxToken> txs = getApi().account().txsNftToken("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67", 4761862, 4761934);
+    void correctStartBlockEndBlock() {
+        List<TxErc721> txs = getApi().account().txsErc721("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67", 4761862, 4761934);
         System.out.println(txs);
         assertNotNull(txs);
         assertEquals(11, txs.size());
         assertTxs(txs);
     }
 
-    @Test(expected = InvalidAddressException.class)
-    public void invalidParamWithError() {
-        getApi().account().txsNftToken("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7");
+    @Test
+    void invalidParamWithError() {
+        assertThrows(EtherScanInvalidAddressException.class,
+                () -> getApi().account().txsErc721("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"));
     }
 
     @Test
-    public void correctParamWithEmptyExpectedResult() {
-        List<TxToken> txs = getApi().account().txsNftToken("0x31ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7");
+    void correctParamWithEmptyExpectedResult() {
+        List<TxErc721> txs = getApi().account().txsErc721("0x31ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7");
         assertNotNull(txs);
         assertTrue(txs.isEmpty());
     }
 
-    private void assertTxs(List<TxToken> txs) {
-        for (TxToken tx : txs) {
+    private void assertTxs(List<TxErc721> txs) {
+        for (TxErc721 tx : txs) {
             assertNotNull(tx.getBlockHash());
             assertNotNull(tx.getTokenName());
             assertNotNull(tx.getTokenSymbol());
@@ -73,7 +73,7 @@ private void assertTxs(List<TxToken> txs) {
             assertNotNull(tx.getTokenDecimal());
             assertNotEquals(-1, (tx.getConfirmations()));
             assertNotNull(tx.getGasUsed());
-            assertNotEquals(-1, tx.getCumulativeGasUsed());
+            assertNotEquals(-1, tx.getGasUsedCumulative().asWei().intValue());
             assertNotEquals(-1, tx.getTransactionIndex());
         }
     }
diff --git a/src/test/java/io/api/etherscan/account/AccountTxsTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxsTests.java
similarity index 70%
rename from src/test/java/io/api/etherscan/account/AccountTxsTest.java
rename to src/test/java/io/goodforgod/api/etherscan/account/AccountTxsTests.java
index 66a95e4..42e89c4 100644
--- a/src/test/java/io/api/etherscan/account/AccountTxsTest.java
+++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxsTests.java
@@ -1,25 +1,22 @@
-package io.api.etherscan.account;
-
-import io.api.ApiRunner;
-import io.api.etherscan.error.InvalidAddressException;
-import io.api.etherscan.model.Tx;
-import org.junit.Test;
+package io.goodforgod.api.etherscan.account;
 
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException;
+import io.goodforgod.api.etherscan.model.Tx;
 import java.util.List;
+import org.junit.jupiter.api.Test;
 
 /**
- * ! NO DESCRIPTION !
- *
  * @author GoodforGod
  * @since 03.11.2018
  */
-public class AccountTxsTest extends ApiRunner {
+class AccountTxsTests extends ApiRunner {
 
     @Test
-    public void correct() {
+    void correct() {
         List<Tx> txs = getApi().account().txs("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9");
         assertNotNull(txs);
-        assertEquals(5, txs.size());
+        assertEquals(6, txs.size());
         assertTxs(txs);
         assertNotNull(txs.get(0).getTimeStamp());
         assertNotNull(txs.get(0).getHash());
@@ -27,7 +24,7 @@ public void correct() {
         assertNotNull(txs.get(0).getTo());
         assertNotNull(txs.get(0).getBlockHash());
         assertNotNull(txs.get(0).getGas());
-        assertNotNull(txs.get(0).getCumulativeGasUsed());
+        assertNotNull(txs.get(0).getGasUsedCumulative());
         assertNotNull(txs.get(0).getGasPrice());
         assertNotNull(txs.get(0).getValue());
         assertNotNull(txs.get(0).getContractAddress());
@@ -39,29 +36,30 @@ public void correct() {
     }
 
     @Test
-    public void correctStartBlock() {
+    void correctStartBlock() {
         List<Tx> txs = getApi().account().txs("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9", 3892842);
         assertNotNull(txs);
-        assertEquals(4, txs.size());
+        assertEquals(5, txs.size());
         assertTxs(txs);
     }
 
     @Test
-    public void correctStartBlockEndBlock() {
+    void correctStartBlockEndBlock() {
         List<Tx> txs = getApi().account().txs("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9", 3892842, 3945741);
         assertNotNull(txs);
         assertEquals(3, txs.size());
         assertTxs(txs);
-        assertFalse(txs.get(0).equals(txs.get(1)));
+        assertNotEquals(txs.get(0), txs.get(1));
     }
 
-    @Test(expected = InvalidAddressException.class)
-    public void invalidParamWithError() {
-        List<Tx> txs = getApi().account().txs("9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9");
+    @Test
+    void invalidParamWithError() {
+        assertThrows(EtherScanInvalidAddressException.class,
+                () -> getApi().account().txs("9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9"));
     }
 
     @Test
-    public void correctParamWithEmptyExpectedResult() {
+    void correctParamWithEmptyExpectedResult() {
         List<Tx> txs = getApi().account().txs("0x9321cb34984c3992ec1EA0eAE98Ccf80A74f95B9");
         assertNotNull(txs);
         assertTrue(txs.isEmpty());
@@ -77,7 +75,7 @@ private void assertTxs(List<Tx> txs) {
             assertNotEquals(-1, (tx.getNonce()));
             assertNotEquals(0, (tx.getTransactionIndex()));
             assertNotEquals(0, tx.getConfirmations());
-            assertNotNull(tx.getTxreceipt_status());
+            assertNotNull(tx.getTxReceiptStatus());
         }
     }
 }
diff --git a/src/test/java/io/api/etherscan/block/BlockApiTest.java b/src/test/java/io/goodforgod/api/etherscan/block/BlockApiTests.java
similarity index 74%
rename from src/test/java/io/api/etherscan/block/BlockApiTest.java
rename to src/test/java/io/goodforgod/api/etherscan/block/BlockApiTests.java
index 34b9de5..7a923aa 100644
--- a/src/test/java/io/api/etherscan/block/BlockApiTest.java
+++ b/src/test/java/io/goodforgod/api/etherscan/block/BlockApiTests.java
@@ -1,22 +1,19 @@
-package io.api.etherscan.block;
-
-import io.api.ApiRunner;
-import io.api.etherscan.model.UncleBlock;
-import org.junit.Test;
+package io.goodforgod.api.etherscan.block;
 
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.model.BlockUncle;
 import java.util.Optional;
+import org.junit.jupiter.api.Test;
 
 /**
- * ! NO DESCRIPTION !
- *
  * @author GoodforGod
  * @since 03.11.2018
  */
-public class BlockApiTest extends ApiRunner {
+class BlockApiTests extends ApiRunner {
 
     @Test
-    public void correct() {
-        Optional<UncleBlock> uncle = getApi().block().uncles(2165403);
+    void correct() {
+        Optional<BlockUncle> uncle = getApi().block().uncles(2165403);
         assertTrue(uncle.isPresent());
         assertFalse(uncle.get().isEmpty());
         assertNotNull(uncle.get().getBlockMiner());
@@ -28,7 +25,7 @@ public void correct() {
         assertNotEquals(-1, uncle.get().getUncles().get(0).getUnclePosition());
         assertNotNull(uncle.get().toString());
 
-        UncleBlock empty = new UncleBlock();
+        BlockUncle empty = BlockUncle.builder().build();
         assertNotEquals(uncle.get().hashCode(), empty.hashCode());
         assertNotEquals(uncle.get(), empty);
         assertTrue(empty.isEmpty());
@@ -46,15 +43,15 @@ public void correct() {
     }
 
     @Test
-    public void correctNoUncles() {
-        Optional<UncleBlock> uncles = getApi().block().uncles(34);
+    void correctNoUncles() {
+        Optional<BlockUncle> uncles = getApi().block().uncles(34);
         assertTrue(uncles.isPresent());
         assertTrue(uncles.get().getUncles().isEmpty());
     }
 
     @Test
-    public void correctParamWithEmptyExpectedResult() {
-        Optional<UncleBlock> uncles = getApi().block().uncles(99999999934L);
+    void correctParamWithEmptyExpectedResult() {
+        Optional<BlockUncle> uncles = getApi().block().uncles(99999999934L);
         assertFalse(uncles.isPresent());
     }
 }
diff --git a/src/test/java/io/goodforgod/api/etherscan/contract/ContractApiTests.java b/src/test/java/io/goodforgod/api/etherscan/contract/ContractApiTests.java
new file mode 100644
index 0000000..d1e4de4
--- /dev/null
+++ b/src/test/java/io/goodforgod/api/etherscan/contract/ContractApiTests.java
@@ -0,0 +1,86 @@
+package io.goodforgod.api.etherscan.contract;
+
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException;
+import io.goodforgod.api.etherscan.model.Abi;
+import io.goodforgod.api.etherscan.model.ContractCreation;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author GoodforGod
+ * @since 03.11.2018
+ */
+class ContractApiTests extends ApiRunner {
+
+    @Test
+    void correct() {
+        Abi abi = getApi().contract().contractAbi("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413");
+        assertNotNull(abi);
+        assertTrue(abi.isVerified());
+        assertTrue(abi.haveAbi());
+        assertNotNull(abi.getContractAbi());
+        assertNotNull(abi.toString());
+
+        Abi empty = Abi.verified("asg");
+        assertNotEquals(empty, abi);
+        assertNotEquals(empty.hashCode(), abi.hashCode());
+    }
+
+    @Test
+    void invalidParamWithError() {
+        assertThrows(EtherScanInvalidAddressException.class,
+                () -> getApi().contract().contractAbi("0xBBbc244D798123fDe783fCc1C72d3Bb8C189413"));
+    }
+
+    @Test
+    void correctParamWithEmptyExpectedResult() {
+        Abi abi = getApi().contract().contractAbi("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413");
+        assertNotNull(abi);
+        assertTrue(abi.isVerified());
+    }
+
+    @Test
+    void correctContractCreation() {
+        List<ContractCreation> contractCreations = getApi().contract()
+                .contractCreation(Collections.singletonList("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413"));
+
+        assertEquals(1, contractCreations.size());
+        ContractCreation contractCreation = contractCreations.get(0);
+
+        assertEquals("0xbb9bc244d798123fde783fcc1c72d3bb8c189413", contractCreation.getContractAddress());
+        assertEquals("0x793ea9692ada1900fbd0b80fffec6e431fe8b391", contractCreation.getContractCreator());
+        assertEquals("0xe9ebfecc2fa10100db51a4408d18193b3ac504584b51a4e55bdef1318f0a30f9", contractCreation.getTxHash());
+    }
+
+    @Test
+    void correctMultipleContractCreation() {
+        List<ContractCreation> contractCreations = getApi().contract().contractCreation(
+                Arrays.asList("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413", "0x5EaC95ad5b287cF44E058dCf694419333b796123"));
+        assertEquals(2, contractCreations.size());
+
+        ContractCreation contractCreation1 = ContractCreation.builder()
+                .withContractAddress("0xbb9bc244d798123fde783fcc1c72d3bb8c189413")
+                .withContractCreator("0x793ea9692ada1900fbd0b80fffec6e431fe8b391")
+                .withTxHash("0xe9ebfecc2fa10100db51a4408d18193b3ac504584b51a4e55bdef1318f0a30f9")
+                .build();
+
+        ContractCreation contractCreation2 = ContractCreation.builder()
+                .withContractAddress("0x5eac95ad5b287cf44e058dcf694419333b796123")
+                .withContractCreator("0x7c675b7450e878e5af8550b41df42d134674e61f")
+                .withTxHash("0x79cdfec19e5a86d9022680a4d1c86d3d8cd76c21c01903a2f02c127a0a7dbfb3")
+                .build();
+
+        assertTrue(contractCreations.contains(contractCreation1));
+        assertTrue(contractCreations.contains(contractCreation2));
+    }
+
+    @Test
+    void contractCreationInvalidParamWithError() {
+        assertThrows(EtherScanInvalidAddressException.class,
+                () -> getApi().contract()
+                        .contractCreation(Collections.singletonList("0xBBbc244D798123fDe783fCc1C72d3Bb8C189414")));
+    }
+}
diff --git a/src/test/java/io/goodforgod/api/etherscan/gastracker/GasTrackerApiTests.java b/src/test/java/io/goodforgod/api/etherscan/gastracker/GasTrackerApiTests.java
new file mode 100644
index 0000000..b309dd9
--- /dev/null
+++ b/src/test/java/io/goodforgod/api/etherscan/gastracker/GasTrackerApiTests.java
@@ -0,0 +1,32 @@
+package io.goodforgod.api.etherscan.gastracker;
+
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.model.GasOracle;
+import io.goodforgod.api.etherscan.model.Wei;
+import java.time.Duration;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author GoodforGod
+ * @since 14.05.2023
+ */
+class GasTrackerApiTests extends ApiRunner {
+
+    @Test
+    void estimate() {
+        Duration estimate = getApi().gasTracker().estimate(Wei.ofWei(123));
+        assertNotNull(estimate);
+    }
+
+    @Test
+    void oracle() {
+        GasOracle oracle = getApi().gasTracker().oracle();
+        assertNotNull(oracle);
+        assertNotNull(oracle.getGasUsedRatio());
+        assertNotNull(oracle.getFastGasPriceInWei());
+        assertNotNull(oracle.getLastBlock());
+        assertNotNull(oracle.getProposeGasPriceInWei());
+        assertNotNull(oracle.getSafeGasPriceInWei());
+        assertNotNull(oracle.getSuggestBaseFee());
+    }
+}
diff --git a/src/test/java/io/goodforgod/api/etherscan/logs/LogQueryBuilderTests.java b/src/test/java/io/goodforgod/api/etherscan/logs/LogQueryBuilderTests.java
new file mode 100644
index 0000000..955443c
--- /dev/null
+++ b/src/test/java/io/goodforgod/api/etherscan/logs/LogQueryBuilderTests.java
@@ -0,0 +1,320 @@
+package io.goodforgod.api.etherscan.logs;
+
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException;
+import io.goodforgod.api.etherscan.error.EtherScanLogQueryException;
+import io.goodforgod.api.etherscan.model.query.*;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author GoodforGod
+ * @since 03.11.2018
+ */
+class LogQueryBuilderTests extends ApiRunner {
+
+    @Test
+    void singleCorrect() {
+        LogQuery single = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c")
+                .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545")
+                .build();
+
+        assertNotNull(single);
+        assertNotNull(single.params());
+    }
+
+    @Test
+    void singleInCorrectAddress() {
+        assertThrows(EtherScanInvalidAddressException.class,
+                () -> LogQuery.builder("033990122638b9132ca29c723bdf037f1a891a70c")
+                        .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545")
+                        .build());
+    }
+
+    @Test
+    void singleInCorrectTopic() {
+        assertThrows(EtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c")
+                .withTopic("6516=")
+                .build());
+    }
+
+    @Test
+    void tupleCorrect() {
+        LogQuery tuple = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c").withBlockFrom(379224)
+                .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
+                        "0x72657075746174696f6e00000000000000000000000000000000000000000000")
+                .setOpTopic0_1(LogOp.AND)
+                .build();
+
+        assertNotNull(tuple);
+        assertNotNull(tuple.params());
+    }
+
+    @Test
+    void tupleInCorrectOp() {
+        assertThrows(EtherScanLogQueryException.class,
+                () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c").withBlockFrom(379224)
+                        .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
+                                "0x72657075746174696f6e00000000000000000000000000000000000000000000")
+                        .setOpTopic0_1(null)
+                        .build());
+    }
+
+    @Test
+    void tripleCorrect() {
+        LogQuery triple = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c").withBlockFrom(379224).withBlockTo(400000)
+                .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
+                        "0x72657075746174696f6e00000000000000000000000000000000000000000000",
+                        "0x72657075746174696f6e00000000000000000000000000000000000000000000")
+                .setOpTopic0_1(LogOp.AND)
+                .setOpTopic0_2(LogOp.OR)
+                .setOpTopic1_2(LogOp.AND)
+                .build();
+
+        assertNotNull(triple);
+        assertNotNull(triple.params());
+    }
+
+    @Test
+    void tripleInCorrectOp() {
+        assertThrows(EtherScanLogQueryException.class,
+                () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c").withBlockFrom(379224).withBlockTo(400000)
+                        .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
+                                "0x72657075746174696f6e00000000000000000000000000000000000000000000",
+                                "0x72657075746174696f6e00000000000000000000000000000000000000000000")
+                        .setOpTopic0_1(LogOp.AND)
+                        .setOpTopic0_2(null)
+                        .setOpTopic1_2(LogOp.AND)
+                        .build());
+    }
+
+    @Test
+    void tripleInCorrectTopic1() {
+        assertThrows(EtherScanLogQueryException.class,
+                () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c").withBlockFrom(379224).withBlockTo(400000)
+                        .withTopic(null,
+                                "0x72657075746174696f6e00000000000000000000000000000000000000000000",
+                                "0x72657075746174696f6e00000000000000000000000000000000000000000000")
+                        .setOpTopic0_1(LogOp.AND)
+                        .setOpTopic0_2(LogOp.AND)
+                        .setOpTopic1_2(LogOp.AND)
+                        .build());
+    }
+
+    @Test
+    void tripleInCorrectTopic2() {
+        assertThrows(EtherScanLogQueryException.class,
+                () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c").withBlockFrom(379224).withBlockTo(400000)
+                        .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
+                                null,
+                                "0x72657075746174696f6e00000000000000000000000000000000000000000000")
+                        .setOpTopic0_1(LogOp.AND)
+                        .setOpTopic0_2(LogOp.AND)
+                        .setOpTopic1_2(LogOp.AND)
+                        .build());
+    }
+
+    @Test
+    void tripleInCorrectTopic3() {
+        assertThrows(EtherScanLogQueryException.class,
+                () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c").withBlockFrom(379224).withBlockTo(400000)
+                        .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
+                                "0x72657075746174696f6e00000000000000000000000000000000000000000000",
+                                null)
+                        .setOpTopic0_1(LogOp.AND)
+                        .setOpTopic0_2(LogOp.AND)
+                        .setOpTopic1_2(LogOp.AND)
+                        .build());
+    }
+
+    @Test
+    void quadroCorrect() {
+        LogQuery quadro = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c")
+                .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
+                        "0x72657075746174696f6e00000000000000000000000000000000000000000000",
+                        "0x72657075746174696f6e00000000000000000000000000000000000000000000",
+                        "0x72657075746174696f6e00000000000000000000000000000000000000000000")
+                .setOpTopic0_1(LogOp.AND)
+                .setOpTopic0_2(LogOp.OR)
+                .setOpTopic0_3(LogOp.AND)
+                .setOpTopic1_2(LogOp.OR)
+                .setOpTopic1_3(LogOp.OR)
+                .setOpTopic2_3(LogOp.OR)
+                .build();
+
+        assertNotNull(quadro);
+        assertNotNull(quadro.params());
+    }
+
+    @Test
+    void quadroIncorrectTopic2() {
+        assertThrows(EtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c")
+                .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
+                        null,
+                        "0x72657075746174696f6e00000000000000000000000000000000000000000000",
+                        "0x72657075746174696f6e00000000000000000000000000000000000000000000")
+                .setOpTopic0_1(LogOp.AND)
+                .setOpTopic0_2(LogOp.OR)
+                .setOpTopic0_3(LogOp.AND)
+                .setOpTopic1_2(LogOp.OR)
+                .setOpTopic1_3(LogOp.OR)
+                .setOpTopic2_3(LogOp.OR)
+                .build());
+    }
+
+    @Test
+    void tupleIncorrectTopic2() {
+        assertThrows(EtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c")
+                .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
+                        null)
+                .setOpTopic0_1(LogOp.AND)
+                .build());
+    }
+
+    @Test
+    void tupleIncorrectTopic1() {
+        assertThrows(EtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c")
+                .withTopic(null,
+                        "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545")
+                .setOpTopic0_1(LogOp.AND)
+                .build());
+    }
+
+    @Test
+    void quadroIncorrectOp1() {
+        final LogTopicQuadro topicQuadro = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c")
+                .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
+                        "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
+                        "0x72657075746174696f6e00000000000000000000000000000000000000000000",
+                        "0x72657075746174696f6e00000000000000000000000000000000000000000000");
+
+        assertThrows(EtherScanLogQueryException.class, () -> topicQuadro
+                .setOpTopic0_1(null)
+                .setOpTopic0_2(LogOp.OR)
+                .setOpTopic0_3(LogOp.AND)
+                .setOpTopic1_2(LogOp.OR)
+                .setOpTopic1_3(LogOp.OR)
+                .setOpTopic2_3(LogOp.OR)
+                .build());
+    }
+
+    @Test
+    void quadroIncorrectOp2() {
+        final LogTopicQuadro topicQuadro = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c")
+                .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
+                        "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
+                        "0x72657075746174696f6e00000000000000000000000000000000000000000000",
+                        "0x72657075746174696f6e00000000000000000000000000000000000000000000");
+
+        assertThrows(EtherScanLogQueryException.class, () -> topicQuadro.setOpTopic0_1(LogOp.AND)
+                .setOpTopic0_2(null)
+                .setOpTopic0_3(LogOp.AND)
+                .setOpTopic1_2(LogOp.OR)
+                .setOpTopic1_3(LogOp.OR)
+                .setOpTopic2_3(LogOp.OR)
+                .build());
+    }
+
+    @Test
+    void quadroIncorrectOp3() {
+        final LogTopicQuadro topicQuadro = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c")
+                .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
+                        "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
+                        "0x72657075746174696f6e00000000000000000000000000000000000000000000",
+                        "0x72657075746174696f6e00000000000000000000000000000000000000000000");
+
+        assertThrows(EtherScanLogQueryException.class, () -> topicQuadro
+                .setOpTopic0_1(LogOp.AND)
+                .setOpTopic0_2(LogOp.OR)
+                .setOpTopic0_3(null)
+                .setOpTopic1_2(LogOp.OR)
+                .setOpTopic1_3(LogOp.OR)
+                .setOpTopic2_3(LogOp.OR)
+                .build());
+    }
+
+    @Test
+    void quadroInCorrectAgainTopic() {
+        assertThrows(EtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c")
+                .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
+                        "0x72657075746174696f6e00000000000000000000000000000000000000000000",
+                        "0x72657075746174696f6e00000000000000000000000000000000000000000000",
+                        null)
+                .setOpTopic0_1(LogOp.AND)
+                .setOpTopic0_2(LogOp.OR)
+                .setOpTopic0_3(LogOp.AND)
+                .setOpTopic1_2(LogOp.OR)
+                .setOpTopic1_3(LogOp.OR)
+                .setOpTopic2_3(LogOp.OR)
+                .build());
+    }
+
+    @Test
+    void quadroInCorrectOp4() {
+        final LogTopicQuadro topicQuadro = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c")
+                .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
+                        "0x72657075746174696f6e00000000000000000000000000000000000000000000",
+                        "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
+                        "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545");
+
+        assertThrows(EtherScanLogQueryException.class, () -> topicQuadro
+                .setOpTopic0_1(LogOp.AND)
+                .setOpTopic0_2(LogOp.OR)
+                .setOpTopic0_3(LogOp.AND)
+                .setOpTopic1_2(null)
+                .setOpTopic1_3(LogOp.OR)
+                .setOpTopic2_3(LogOp.OR)
+                .build());
+    }
+
+    @Test
+    void quadroInCorrectOp5() {
+        final LogTopicQuadro topicQuadro = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c")
+                .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
+                        "0x72657075746174696f6e00000000000000000000000000000000000000000000",
+                        "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
+                        "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545");
+
+        assertThrows(EtherScanLogQueryException.class, () -> topicQuadro
+                .setOpTopic0_1(LogOp.AND)
+                .setOpTopic0_2(LogOp.OR)
+                .setOpTopic0_3(LogOp.AND)
+                .setOpTopic1_2(LogOp.AND)
+                .setOpTopic1_3(null)
+                .setOpTopic2_3(LogOp.OR)
+                .build());
+    }
+
+    @Test
+    void quadroInCorrectOp6() {
+        LogTopicQuadro topicQuadro = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c")
+                .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
+                        "0x72657075746174696f6e00000000000000000000000000000000000000000000",
+                        "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
+                        "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545");
+
+        assertThrows(EtherScanLogQueryException.class, () -> topicQuadro
+                .setOpTopic0_1(LogOp.AND)
+                .setOpTopic0_2(LogOp.OR)
+                .setOpTopic0_3(LogOp.AND)
+                .setOpTopic1_2(LogOp.AND)
+                .setOpTopic1_3(LogOp.OR)
+                .setOpTopic2_3(null)
+                .build());
+    }
+
+    @Test
+    void quadroInCorrectTopic() {
+        assertThrows(EtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c")
+                .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
+                        "0x72657075746174696f6e00000000000000000000000000000000000000000000",
+                        "",
+                        "")
+                .setOpTopic0_1(LogOp.AND)
+                .setOpTopic0_2(LogOp.OR)
+                .setOpTopic0_3(LogOp.AND)
+                .setOpTopic1_2(LogOp.OR)
+                .setOpTopic1_3(LogOp.OR)
+                .setOpTopic2_3(LogOp.OR)
+                .build());
+    }
+}
diff --git a/src/test/java/io/goodforgod/api/etherscan/logs/LogsApiTests.java b/src/test/java/io/goodforgod/api/etherscan/logs/LogsApiTests.java
new file mode 100644
index 0000000..0197c5f
--- /dev/null
+++ b/src/test/java/io/goodforgod/api/etherscan/logs/LogsApiTests.java
@@ -0,0 +1,75 @@
+package io.goodforgod.api.etherscan.logs;
+
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.model.Log;
+import io.goodforgod.api.etherscan.model.query.LogOp;
+import io.goodforgod.api.etherscan.model.query.LogQuery;
+import java.util.List;
+import java.util.stream.Stream;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+/**
+ * @author GoodforGod
+ * @since 03.11.2018
+ */
+class LogsApiTests extends ApiRunner {
+
+    static Stream<Arguments> source() {
+        LogQuery single = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c")
+                .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545")
+                .build();
+
+        LogQuery singleInvalidAddr = LogQuery.builder("0x13990122638b9132ca29c723bdf037f1a891a70c")
+                .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545")
+                .build();
+
+        LogQuery tupleAnd = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c").withBlockFrom(379224)
+                .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
+                        "0x72657075746174696f6e00000000000000000000000000000000000000000000")
+                .setOpTopic0_1(LogOp.AND)
+                .build();
+
+        LogQuery tupleOr = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c")
+                .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545",
+                        "0x72657075746174696f6e00000000000000000000000000000000000000000000")
+                .setOpTopic0_1(LogOp.OR)
+                .build();
+
+        return Stream.of(
+                Arguments.of(single, 424),
+                Arguments.of(singleInvalidAddr, 0),
+                Arguments.of(tupleAnd, 1),
+                Arguments.of(tupleOr, 426));
+    }
+
+    @ParameterizedTest
+    @MethodSource("source")
+    void validateQuery(LogQuery query, int logsSize) {
+        List<Log> logs = getApi().logs().logs(query);
+        assertEquals(logsSize, logs.size());
+
+        if (logsSize > 0) {
+            if (logsSize > 1) {
+                assertNotEquals(logs.get(0), logs.get(1));
+                assertNotEquals(logs.get(0).hashCode(), logs.get(1).hashCode());
+            }
+
+            assertNotNull(logs.get(0).getAddress());
+            assertNotNull(logs.get(0).getBlockNumber());
+            assertNotNull(logs.get(0).getData());
+            assertNotNull(logs.get(0).getTimeStamp());
+            assertNotNull(logs.get(0).getTransactionHash());
+            assertNotNull(logs.get(0).getTransactionIndex());
+            assertNotNull(logs.get(0).getGasUsed());
+            assertNotNull(logs.get(0).getTopics());
+            assertNotNull(logs.get(0).getLogIndex());
+            assertNotNull(logs.get(0).getGasPrice());
+            assertNotNull(logs.get(0).toString());
+
+            assertEquals(logs.get(0), logs.get(0));
+            assertEquals(logs.get(0).hashCode(), logs.get(0).hashCode());
+        }
+    }
+}
diff --git a/src/test/java/io/goodforgod/api/etherscan/manager/SemaphoreRequestQueueManagerTests.java b/src/test/java/io/goodforgod/api/etherscan/manager/SemaphoreRequestQueueManagerTests.java
new file mode 100644
index 0000000..183c442
--- /dev/null
+++ b/src/test/java/io/goodforgod/api/etherscan/manager/SemaphoreRequestQueueManagerTests.java
@@ -0,0 +1,45 @@
+package io.goodforgod.api.etherscan.manager;
+
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.manager.impl.FakeRequestQueueManager;
+import io.goodforgod.api.etherscan.manager.impl.SemaphoreRequestQueueManager;
+import java.time.Duration;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
+
+/**
+ * @author GoodforGod
+ * @since 03.11.2018
+ */
+class SemaphoreRequestQueueManagerTests extends ApiRunner {
+
+    @Test
+    void fakeManager() {
+        RequestQueueManager fakeManager = new FakeRequestQueueManager();
+        fakeManager.takeTurn();
+        fakeManager.takeTurn();
+        fakeManager.takeTurn();
+        fakeManager.takeTurn();
+        fakeManager.takeTurn();
+        fakeManager.takeTurn();
+        assertNotNull(fakeManager);
+    }
+
+    @Test
+    @Timeout(3500)
+    void queueManager() {
+        RequestQueueManager requestQueueManager = new SemaphoreRequestQueueManager(1, Duration.ofSeconds(3));
+        requestQueueManager.takeTurn();
+        requestQueueManager.takeTurn();
+        assertNotNull(requestQueueManager);
+    }
+
+    @Test
+    @Timeout(4500)
+    void queueManagerWithDelay() {
+        RequestQueueManager requestQueueManager = new SemaphoreRequestQueueManager(1, Duration.ofSeconds(2));
+        requestQueueManager.takeTurn();
+        requestQueueManager.takeTurn();
+        assertNotNull(requestQueueManager);
+    }
+}
diff --git a/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java b/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java
new file mode 100644
index 0000000..8f9a728
--- /dev/null
+++ b/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java
@@ -0,0 +1,510 @@
+package io.goodforgod.api.etherscan.model;
+
+import io.goodforgod.api.etherscan.model.proxy.BlockProxy;
+import io.goodforgod.api.etherscan.model.proxy.ReceiptProxy;
+import io.goodforgod.api.etherscan.model.proxy.TxProxy;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.time.LocalDateTime;
+import java.util.Arrays;
+import java.util.Collections;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author Anton Kurako (GoodforGod)
+ * @since 14.05.2023
+ */
+class ModelBuilderTests extends Assertions {
+
+    @Test
+    void abiBuilder() {
+        Abi value = Abi.builder()
+                .withContractAbi("1")
+                .withIsVerified(true)
+                .build();
+
+        assertNotNull(value);
+        assertTrue(value.isVerified());
+        assertEquals("1", value.getContractAbi());
+    }
+
+    @Test
+    void blockBuilder() {
+        LocalDateTime timestamp = LocalDateTime.now();
+        Block value = Block.builder()
+                .withBlockNumber(1)
+                .withBlockReward(BigInteger.ONE)
+                .withTimeStamp(timestamp)
+                .build();
+
+        assertNotNull(value);
+        assertEquals(1, value.getBlockNumber());
+        assertEquals(BigInteger.ONE, value.getBlockReward());
+        assertEquals(timestamp, value.getTimeStamp());
+    }
+
+    @Test
+    void blockUncleBuilder() {
+        LocalDateTime timestamp = LocalDateTime.now();
+        BlockUncle value = BlockUncle.builder()
+                .withBlockNumber(1)
+                .withBlockReward(BigInteger.ONE)
+                .withTimeStamp(timestamp)
+                .withBlockMiner("1")
+                .withUncleInclusionReward("1")
+                .withUncles(Collections.singletonList(BlockUncle.Uncle.builder()
+                        .withBlockreward(BigInteger.ONE)
+                        .withMiner("1")
+                        .withUnclePosition(1)
+                        .build()))
+                .build();
+
+        assertNotNull(value);
+        assertEquals(1, value.getBlockNumber());
+        assertEquals(BigInteger.ONE, value.getBlockReward());
+        assertEquals(timestamp, value.getTimeStamp());
+    }
+
+    @Test
+    void gasOracleBuilder() {
+        GasOracle value = GasOracle.builder()
+                .withFastGasPrice(Wei.ofWei(1000000000))
+                .withProposeGasPrice(Wei.ofWei(1000000000))
+                .withSafeGasPrice(Wei.ofWei(1000000000))
+                .withGasUsedRatio(Collections.singletonList(new BigDecimal(1)))
+                .withLastBlock(1L)
+                .withSuggestBaseFee(BigDecimal.valueOf(1.0))
+                .build();
+
+        assertNotNull(value);
+        assertEquals(Wei.ofWei(1000000000), value.getFastGasPriceInWei());
+
+        GasOracle value2 = GasOracle.builder()
+                .withFastGasPrice(Wei.ofWei(1000000000))
+                .withProposeGasPrice(Wei.ofWei(1000000000))
+                .withSafeGasPrice(Wei.ofWei(1000000000))
+                .withGasUsedRatio(Collections.singletonList(new BigDecimal(1)))
+                .withLastBlock(1L)
+                .withSuggestBaseFee(BigDecimal.valueOf(1.0))
+                .build();
+        assertEquals(value, value2);
+        assertEquals(value.hashCode(), value2.hashCode());
+        assertEquals(value.toString(), value2.toString());
+    }
+
+    @Test
+    void logBuilder() {
+        LocalDateTime timestamp = LocalDateTime.now();
+        Log value = Log.builder()
+                .withAddress("1")
+                .withBlockNumber(1L)
+                .withData("1")
+                .withGasPrice(Wei.ofWei(1))
+                .withGasUsed(Wei.ofWei(1))
+                .withLogIndex(1L)
+                .withTimeStamp(timestamp)
+                .withTransactionHash("1")
+                .withTransactionIndex(1L)
+                .withTopics(Collections.singletonList("1"))
+                .build();
+
+        assertNotNull(value);
+        assertEquals(1, value.getTopics().size());
+    }
+
+    @Test
+    void priceBuilder() {
+        LocalDateTime timestamp = LocalDateTime.now();
+        Price value = Price.builder()
+                .withBtc(BigDecimal.valueOf(1.0))
+                .withUsd(BigDecimal.valueOf(1.0))
+                .withTimestampBtc(timestamp)
+                .withTimestampUsd(timestamp)
+                .build();
+
+        assertNotNull(value);
+        assertEquals(BigDecimal.valueOf(1.0), value.inUsd());
+        assertEquals(BigDecimal.valueOf(1.0), value.inBtc());
+    }
+
+    @Test
+    void statusBuilder() {
+        Status value = Status.builder()
+                .withIsError(1)
+                .withErrDescription("1")
+                .build();
+
+        assertNotNull(value);
+        assertTrue(value.haveError());
+        assertEquals("1", value.getErrDescription());
+    }
+
+    @Test
+    void txBuilder() {
+        LocalDateTime timestamp = LocalDateTime.now();
+        Tx value = Tx.builder()
+                .withBlockHash("1")
+                .withBlockNumber(1L)
+                .withConfirmations(1L)
+                .withContractAddress("1")
+                .withFrom("1")
+                .withTo("1")
+                .withCumulativeGasUsed(Wei.ofWei(BigInteger.ONE))
+                .withGas(Wei.ofWei(BigInteger.ONE))
+                .withGasPrice(Wei.ofWei(BigInteger.ONE))
+                .withGasUsed(Wei.ofWei(BigInteger.ONE))
+                .withHash("1")
+                .withInput("1")
+                .withIsError("1")
+                .withNonce(1L)
+                .withTimeStamp(timestamp)
+                .withValue(BigInteger.ONE)
+                .withTransactionIndex(1)
+                .withTxReceiptStatus("1")
+                .build();
+
+        assertNotNull(value);
+        assertTrue(value.haveError());
+        assertEquals("1", value.getTo());
+        assertEquals("1", value.getFrom());
+
+        Tx value2 = Tx.builder()
+                .withBlockHash("1")
+                .withBlockNumber(1L)
+                .withConfirmations(1L)
+                .withContractAddress("1")
+                .withFrom("1")
+                .withTo("1")
+                .withCumulativeGasUsed(Wei.ofWei(BigInteger.ONE))
+                .withGas(Wei.ofWei(BigInteger.ONE))
+                .withGasPrice(Wei.ofWei(BigInteger.ONE))
+                .withGasUsed(Wei.ofWei(BigInteger.ONE))
+                .withHash("1")
+                .withInput("1")
+                .withIsError("1")
+                .withNonce(1L)
+                .withTimeStamp(timestamp)
+                .withValue(BigInteger.ONE)
+                .withTransactionIndex(1)
+                .withTxReceiptStatus("1")
+                .build();
+
+        assertEquals(value, value2);
+        assertEquals(value.hashCode(), value2.hashCode());
+        assertEquals(value.toString(), value2.toString());
+        assertEquals(0, value.compareTo(value2));
+    }
+
+    @Test
+    void txErc20Builder() {
+        LocalDateTime timestamp = LocalDateTime.now();
+        TxErc20 value = TxErc20.builder()
+                .withBlockHash("1")
+                .withBlockNumber(1L)
+                .withConfirmations(1L)
+                .withContractAddress("1")
+                .withFrom("1")
+                .withTo("1")
+                .withCumulativeGasUsed(Wei.ofWei(BigInteger.ONE))
+                .withGas(Wei.ofWei(BigInteger.ONE))
+                .withGasPrice(Wei.ofWei(BigInteger.ONE))
+                .withGasUsed(Wei.ofWei(BigInteger.ONE))
+                .withHash("1")
+                .withInput("1")
+                .withTokenName("1")
+                .withTokenSymbol("1")
+                .withTokenDecimal("1")
+                .withNonce(1L)
+                .withTimeStamp(timestamp)
+                .withValue(BigInteger.ONE)
+                .withTransactionIndex(1)
+                .build();
+
+        assertNotNull(value);
+        assertEquals("1", value.getTo());
+        assertEquals("1", value.getFrom());
+    }
+
+    @Test
+    void txErc721Builder() {
+        LocalDateTime timestamp = LocalDateTime.now();
+        TxErc721 value = TxErc721.builder()
+                .withBlockHash("1")
+                .withBlockNumber(1L)
+                .withConfirmations(1L)
+                .withContractAddress("1")
+                .withFrom("1")
+                .withTo("1")
+                .withCumulativeGasUsed(Wei.ofWei(BigInteger.ONE))
+                .withGas(Wei.ofWei(BigInteger.ONE))
+                .withGasPrice(Wei.ofWei(BigInteger.ONE))
+                .withGasUsed(Wei.ofWei(BigInteger.ONE))
+                .withHash("1")
+                .withInput("1")
+                .withTokenName("1")
+                .withTokenSymbol("1")
+                .withTokenDecimal("1")
+                .withTokenID("1")
+                .withNonce(1L)
+                .withTimeStamp(timestamp)
+                .withTransactionIndex(1)
+                .build();
+
+        assertNotNull(value);
+        assertEquals("1", value.getTo());
+        assertEquals("1", value.getFrom());
+    }
+
+    @Test
+    void txErc1155Builder() {
+        LocalDateTime timestamp = LocalDateTime.now();
+        TxErc1155 value = TxErc1155.builder()
+                .withBlockHash("1")
+                .withBlockNumber(1L)
+                .withConfirmations(1L)
+                .withContractAddress("1")
+                .withFrom("1")
+                .withTo("1")
+                .withCumulativeGasUsed(Wei.ofWei(BigInteger.ONE))
+                .withGas(Wei.ofWei(BigInteger.ONE))
+                .withGasPrice(Wei.ofWei(BigInteger.ONE))
+                .withGasUsed(Wei.ofWei(BigInteger.ONE))
+                .withHash("1")
+                .withInput("1")
+                .withTokenName("1")
+                .withTokenSymbol("1")
+                .withTokenDecimal("1")
+                .withTokenID("1")
+                .withNonce(1L)
+                .withTimeStamp(timestamp)
+                .withTransactionIndex(1)
+                .build();
+
+        assertNotNull(value);
+        assertEquals("1", value.getTo());
+        assertEquals("1", value.getFrom());
+
+        TxErc1155 value2 = TxErc1155.builder()
+                .withBlockHash("1")
+                .withBlockNumber(1L)
+                .withConfirmations(1L)
+                .withContractAddress("1")
+                .withFrom("1")
+                .withTo("1")
+                .withCumulativeGasUsed(Wei.ofWei(BigInteger.ONE))
+                .withGas(Wei.ofWei(BigInteger.ONE))
+                .withGasPrice(Wei.ofWei(BigInteger.ONE))
+                .withGasUsed(Wei.ofWei(BigInteger.ONE))
+                .withHash("1")
+                .withInput("1")
+                .withTokenName("1")
+                .withTokenSymbol("1")
+                .withTokenDecimal("1")
+                .withTokenID("1")
+                .withNonce(1L)
+                .withTimeStamp(timestamp)
+                .withTransactionIndex(1)
+                .build();
+
+        assertEquals(value, value2);
+        assertEquals(value.hashCode(), value2.hashCode());
+        assertEquals(value.toString(), value2.toString());
+    }
+
+    @Test
+    void txInternalBuilder() {
+        LocalDateTime timestamp = LocalDateTime.now();
+        TxInternal value = TxInternal.builder()
+                .withBlockNumber(1L)
+                .withContractAddress("1")
+                .withFrom("1")
+                .withTo("1")
+                .withValue(BigInteger.ONE)
+                .withGas(Wei.ofWei(BigInteger.ONE))
+                .withGasUsed(Wei.ofWei(BigInteger.ONE))
+                .withHash("1")
+                .withInput("1")
+                .withTimeStamp(timestamp)
+                .withErrCode("1")
+                .withIsError(1)
+                .withTraceId("1")
+                .withType("1")
+                .build();
+
+        assertNotNull(value);
+        assertEquals("1", value.getTo());
+        assertEquals("1", value.getFrom());
+
+        TxInternal value2 = TxInternal.builder()
+                .withBlockNumber(1L)
+                .withContractAddress("1")
+                .withFrom("1")
+                .withTo("1")
+                .withValue(BigInteger.ONE)
+                .withGas(Wei.ofWei(BigInteger.ONE))
+                .withGasUsed(Wei.ofWei(BigInteger.ONE))
+                .withHash("1")
+                .withInput("1")
+                .withTimeStamp(timestamp)
+                .withErrCode("1")
+                .withIsError(1)
+                .withTraceId("1")
+                .withType("1")
+                .build();
+
+        assertEquals(value, value2);
+        assertEquals(value.hashCode(), value2.hashCode());
+        assertEquals(value.toString(), value2.toString());
+    }
+
+    @Test
+    void ethSupplyBuilder() {
+        EthSupply value = EthSupply.builder()
+                .withBurntFees(Wei.ofWei(1))
+                .withEth2Staking(Wei.ofWei(1))
+                .withEthSupply(Wei.ofWei(1))
+                .withWithdrawnTotal(Wei.ofWei(1))
+                .build();
+
+        assertNotNull(value);
+        assertEquals(BigInteger.valueOf(1), value.getTotal().asWei());
+
+        EthSupply valueEmpty = EthSupply.builder()
+                .build();
+        assertNotNull(valueEmpty);
+        assertEquals(BigInteger.ZERO, valueEmpty.getTotal().asWei());
+
+        EthSupply value2 = EthSupply.builder()
+                .withBurntFees(Wei.ofWei(1))
+                .withEth2Staking(Wei.ofWei(1))
+                .withEthSupply(Wei.ofWei(1))
+                .withWithdrawnTotal(Wei.ofWei(1))
+                .build();
+        assertEquals(value, value2);
+        assertEquals(value.hashCode(), value2.hashCode());
+        assertEquals(value.toString(), value2.toString());
+    }
+
+    @Test
+    void receiptProxyBuilder() {
+        LocalDateTime timestamp = LocalDateTime.now();
+        ReceiptProxy value = ReceiptProxy.builder()
+                .withBlockHash("1")
+                .withBlockNumber(1L)
+                .withContractAddress("1")
+                .withCumulativeGasUsed(Wei.ofWei(1))
+                .withFrom("1")
+                .withTo("1")
+                .withGasUsed(Wei.ofWei(1))
+                .withRoot("1")
+                .withLogsBloom("1")
+                .withTransactionHash("1")
+                .withTransactionIndex(1L)
+                .withLogs(Arrays.asList(Log.builder()
+                        .withTopics(Arrays.asList("1"))
+                        .withTransactionIndex(1L)
+                        .withTransactionHash("1")
+                        .withTimeStamp(timestamp)
+                        .withLogIndex(1L)
+                        .withGasUsed(Wei.ofWei(1))
+                        .withGasPrice(Wei.ofWei(1))
+                        .withData("1")
+                        .withAddress("1")
+                        .build()))
+                .build();
+
+        assertNotNull(value);
+        assertEquals(BigInteger.valueOf(1), value.getGasUsed().asWei());
+    }
+
+    @Test
+    void blockProxyBuilder() {
+        LocalDateTime timestamp = LocalDateTime.now();
+        BlockProxy value = BlockProxy.builder()
+                .withGasUsed(Wei.ofWei(1))
+                .withLogsBloom("1")
+                .withDifficulty("1")
+                .withExtraData("1")
+                .withGasLimit(Wei.ofWei(1))
+                .withHash("1")
+                .withMiner("1")
+                .withMixHash("1")
+                .withNonce("1")
+                .withNumber(1L)
+                .withParentHash("1")
+                .withReceiptsRoot("1")
+                .withSha3Uncles("1")
+                .withSize(1L)
+                .withStateRoot("1")
+                .withTimestamp(timestamp)
+                .withTotalDifficulty("1")
+                .withTransactionsRoot("1")
+                .withUncles(Arrays.asList("1"))
+                .withTransactions(Arrays.asList(TxProxy.builder()
+                        .withBlockHash("1")
+                        .withBlockNumber(1L)
+                        .withFrom("1")
+                        .withGas(Wei.ofWei(1))
+                        .withGasPrice(Wei.ofWei(1))
+                        .withHash("1")
+                        .withInput("1")
+                        .withNonce(1L)
+                        .withR("1")
+                        .withS("1")
+                        .withTo("1")
+                        .withTransactionIndex(1L)
+                        .withV("1")
+                        .withValue("1")
+                        .withV("1")
+                        .build()))
+                .build();
+
+        assertNotNull(value);
+        assertEquals(BigInteger.valueOf(1), value.getGasUsed().asWei());
+    }
+
+    @Test
+    void weiTests() {
+        Wei w1 = Wei.ofWei(1);
+        Wei w2 = Wei.ofWei(1L);
+        Wei w3 = Wei.ofWei(BigInteger.valueOf(1));
+        assertEquals(w1, w2);
+        assertEquals(w1, w3);
+        assertEquals(w1.hashCode(), w2.hashCode());
+        assertEquals(w1.hashCode(), w3.hashCode());
+        assertEquals(w1.toString(), w3.toString());
+
+        Wei kw1 = Wei.ofKwei(1);
+        Wei kw2 = Wei.ofKwei(1L);
+        Wei kw3 = Wei.ofKwei(BigInteger.valueOf(1));
+        Wei kw4 = Wei.ofKwei(BigDecimal.valueOf(1));
+        assertEquals(kw1, kw2);
+        assertEquals(kw1, kw3);
+        assertEquals(kw1, kw4);
+
+        Wei mw1 = Wei.ofMwei(1);
+        Wei mw2 = Wei.ofMwei(1L);
+        Wei mw3 = Wei.ofMwei(BigInteger.valueOf(1));
+        Wei mw4 = Wei.ofMwei(BigDecimal.valueOf(1));
+        assertEquals(mw1, mw2);
+        assertEquals(mw1, mw3);
+        assertEquals(mw1, mw4);
+
+        Wei gw1 = Wei.ofGwei(1);
+        Wei gw2 = Wei.ofGwei(1L);
+        Wei gw3 = Wei.ofGwei(BigInteger.valueOf(1));
+        Wei gw4 = Wei.ofGwei(BigDecimal.valueOf(1));
+        assertEquals(gw1, gw2);
+        assertEquals(gw1, gw3);
+        assertEquals(gw1, gw4);
+
+        Wei ew1 = Wei.ofEther(1);
+        Wei ew2 = Wei.ofEther(1L);
+        Wei ew3 = Wei.ofEther(BigInteger.valueOf(1));
+        Wei ew4 = Wei.ofEther(BigDecimal.valueOf(1));
+        assertEquals(ew1, ew2);
+        assertEquals(ew1, ew3);
+        assertEquals(ew1, ew4);
+    }
+}
diff --git a/src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTests.java
similarity index 57%
rename from src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java
rename to src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTests.java
index 5d3884d..53ed4cd 100644
--- a/src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java
+++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTests.java
@@ -1,32 +1,19 @@
-package io.api.etherscan.proxy;
-
-import io.api.ApiRunner;
-import io.api.etherscan.core.impl.EtherScanApi;
-import io.api.etherscan.manager.impl.QueueManager;
-import io.api.etherscan.model.EthNetwork;
-import io.api.etherscan.model.proxy.BlockProxy;
-import org.junit.Test;
+package io.goodforgod.api.etherscan.proxy;
 
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.model.proxy.BlockProxy;
 import java.util.Optional;
+import org.junit.jupiter.api.Test;
 
 /**
- * ! NO DESCRIPTION !
- *
  * @author GoodforGod
  * @since 03.11.2018
  */
-public class ProxyBlockApiTest extends ApiRunner {
-
-    private final EtherScanApi api;
-
-    public ProxyBlockApiTest() {
-        final QueueManager queueManager = new QueueManager(1, 5100L, 5100L, 0);
-        this.api = new EtherScanApi(getApiKey(), EthNetwork.MAINNET, queueManager);
-    }
+class ProxyBlockApiTests extends ApiRunner {
 
     @Test
-    public void correct() {
-        Optional<BlockProxy> block = api.proxy().block(5120);
+    void correct() {
+        Optional<BlockProxy> block = getApi().proxy().block(5120);
         assertTrue(block.isPresent());
         BlockProxy proxy = block.get();
         assertNotNull(proxy.getHash());
@@ -35,7 +22,6 @@ public void correct() {
         assertNotNull(proxy.getStateRoot());
         assertNotNull(proxy.getSize());
         assertNotNull(proxy.getDifficulty());
-        assertNotNull(proxy.getTotalDifficulty());
         assertNotNull(proxy.getTimeStamp());
         assertNotNull(proxy.getMiner());
         assertNotNull(proxy.getNonce());
@@ -52,20 +38,20 @@ public void correct() {
         assertNotNull(proxy.getUncles());
         assertNotNull(proxy.toString());
 
-        BlockProxy empty = new BlockProxy();
+        BlockProxy empty = BlockProxy.builder().build();
         assertNotEquals(proxy, empty);
         assertNotEquals(proxy.hashCode(), empty.hashCode());
     }
 
     @Test
-    public void correctParamWithEmptyExpectedResult() {
-        Optional<BlockProxy> block = api.proxy().block(99999999999L);
+    void correctParamWithEmptyExpectedResult() {
+        Optional<BlockProxy> block = getApi().proxy().block(99999999999L);
         assertFalse(block.isPresent());
     }
 
     @Test
-    public void correctParamNegativeNo() {
-        Optional<BlockProxy> block = api.proxy().block(-1);
+    void correctParamNegativeNo() {
+        Optional<BlockProxy> block = getApi().proxy().block(-1);
         assertTrue(block.isPresent());
         assertNotNull(block.get().getHash());
     }
diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockLastNoApiTests.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockLastNoApiTests.java
new file mode 100644
index 0000000..568d9ae
--- /dev/null
+++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockLastNoApiTests.java
@@ -0,0 +1,17 @@
+package io.goodforgod.api.etherscan.proxy;
+
+import io.goodforgod.api.etherscan.ApiRunner;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author GoodforGod
+ * @since 13.11.2018
+ */
+class ProxyBlockLastNoApiTests extends ApiRunner {
+
+    @Test
+    void correct() {
+        long noLast = getApi().proxy().blockNoLast();
+        assertNotEquals(0, noLast);
+    }
+}
diff --git a/src/test/java/io/api/etherscan/proxy/ProxyBlockUncleApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockUncleApiTests.java
similarity index 62%
rename from src/test/java/io/api/etherscan/proxy/ProxyBlockUncleApiTest.java
rename to src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockUncleApiTests.java
index 474c5bb..01725c5 100644
--- a/src/test/java/io/api/etherscan/proxy/ProxyBlockUncleApiTest.java
+++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockUncleApiTests.java
@@ -1,21 +1,18 @@
-package io.api.etherscan.proxy;
-
-import io.api.ApiRunner;
-import io.api.etherscan.model.proxy.BlockProxy;
-import org.junit.Test;
+package io.goodforgod.api.etherscan.proxy;
 
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.model.proxy.BlockProxy;
 import java.util.Optional;
+import org.junit.jupiter.api.Test;
 
 /**
- * ! NO DESCRIPTION !
- *
  * @author GoodforGod
  * @since 13.11.2018
  */
-public class ProxyBlockUncleApiTest extends ApiRunner {
+class ProxyBlockUncleApiTests extends ApiRunner {
 
     @Test
-    public void correct() {
+    void correct() {
         Optional<BlockProxy> block = getApi().proxy().blockUncle(603183, 0);
         assertTrue(block.isPresent());
         assertNotNull(block.get().getHash());
@@ -23,13 +20,13 @@ public void correct() {
     }
 
     @Test
-    public void correctParamWithEmptyExpectedResult() {
+    void correctParamWithEmptyExpectedResult() {
         Optional<BlockProxy> block = getApi().proxy().blockUncle(5120, 1);
         assertFalse(block.isPresent());
     }
 
     @Test
-    public void correctParamNegativeNo() {
+    void correctParamNegativeNo() {
         Optional<BlockProxy> block = getApi().proxy().blockUncle(-603183, 0);
         assertFalse(block.isPresent());
     }
diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCallApiTests.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCallApiTests.java
new file mode 100644
index 0000000..d5168c6
--- /dev/null
+++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCallApiTests.java
@@ -0,0 +1,45 @@
+package io.goodforgod.api.etherscan.proxy;
+
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException;
+import io.goodforgod.api.etherscan.error.EtherScanInvalidDataHexException;
+import io.goodforgod.api.etherscan.util.BasicUtils;
+import java.util.Optional;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author GoodforGod
+ * @since 03.11.2018
+ */
+class ProxyCallApiTests extends ApiRunner {
+
+    @Test
+    void correct() {
+        Optional<String> call = getApi().proxy().call("0xAEEF46DB4855E25702F8237E8f403FddcaF931C0",
+                "0x70a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724");
+        assertTrue(call.isPresent());
+        assertFalse(BasicUtils.isNotHex(call.get()));
+    }
+
+    @Test
+    void invalidParamWithError() {
+        assertThrows(EtherScanInvalidAddressException.class,
+                () -> getApi().proxy().call("0xEEF46DB4855E25702F8237E8f403FddcaF931C0",
+                        "0x70a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724"));
+    }
+
+    @Test
+    void invalidParamNotHex() {
+        assertThrows(EtherScanInvalidDataHexException.class,
+                () -> getApi().proxy().call("0xAEEF46DB4855E25702F8237E8f403FddcaF931C0",
+                        "7-0a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724"));
+    }
+
+    @Test
+    void correctParamWithEmptyExpectedResult() {
+        Optional<String> call = getApi().proxy().call("0xAEEF16DB4855E25702F8237E8f403FddcaF931C0",
+                "0x70a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724");
+        assertTrue(call.isPresent());
+        assertFalse(BasicUtils.isNotHex(call.get()), call.get());
+    }
+}
diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCodeApiTests.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCodeApiTests.java
new file mode 100644
index 0000000..1e3c696
--- /dev/null
+++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCodeApiTests.java
@@ -0,0 +1,34 @@
+package io.goodforgod.api.etherscan.proxy;
+
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException;
+import io.goodforgod.api.etherscan.util.BasicUtils;
+import java.util.Optional;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author GoodforGod
+ * @since 03.11.2018
+ */
+class ProxyCodeApiTests extends ApiRunner {
+
+    @Test
+    void correct() {
+        Optional<String> call = getApi().proxy().code("0xf75e354c5edc8efed9b59ee9f67a80845ade7d0c");
+        assertTrue(call.isPresent());
+        assertFalse(BasicUtils.isNotHex(call.get()), call.get());
+    }
+
+    @Test
+    void invalidParamWithError() {
+        assertThrows(EtherScanInvalidAddressException.class,
+                () -> getApi().proxy().code("0f75e354c5edc8efed9b59ee9f67a80845ade7d0c"));
+    }
+
+    @Test
+    void correctParamWithEmptyExpectedResult() {
+        Optional<String> call = getApi().proxy().code("0xf15e354c5edc8efed9b59ee9f67a80845ade7d0c");
+        assertTrue(call.isPresent());
+        assertFalse(BasicUtils.isNotHex(call.get()), call.get());
+    }
+}
diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyGasApiTests.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyGasApiTests.java
new file mode 100644
index 0000000..4dea82e
--- /dev/null
+++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyGasApiTests.java
@@ -0,0 +1,43 @@
+package io.goodforgod.api.etherscan.proxy;
+
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.error.EtherScanInvalidDataHexException;
+import io.goodforgod.api.etherscan.model.Wei;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author GoodforGod
+ * @since 03.11.2018
+ */
+class ProxyGasApiTests extends ApiRunner {
+
+    @Test
+    void correctPrice() {
+        Wei price = getApi().proxy().gasPrice();
+        assertNotNull(price);
+        assertNotEquals(0, price.asWei().intValue());
+    }
+
+    @Test
+    void correctEstimated() {
+        Wei price = getApi().proxy().gasEstimated();
+        assertNotNull(price);
+        assertNotEquals(0, price.asWei().intValue());
+    }
+
+    @Test
+    void correctEstimatedWithData() {
+        String dataCustom = "606060405260728060106000396000f360606040526000606060405260728060106000396000f360606040526000";
+        Wei price = getApi().proxy().gasEstimated();
+        Wei priceCustom = getApi().proxy().gasEstimated(dataCustom);
+        assertNotNull(price);
+        assertNotNull(priceCustom);
+        assertNotEquals(price, priceCustom);
+    }
+
+    @Test
+    void invalidParamWithError() {
+        String dataCustom = "280&60106000396000f360606040526000";
+        assertThrows(EtherScanInvalidDataHexException.class, () -> getApi().proxy().gasEstimated(dataCustom));
+    }
+}
diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyStorageApiTests.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyStorageApiTests.java
new file mode 100644
index 0000000..3c6d221
--- /dev/null
+++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyStorageApiTests.java
@@ -0,0 +1,31 @@
+package io.goodforgod.api.etherscan.proxy;
+
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException;
+import java.util.Optional;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author GoodforGod
+ * @since 03.11.2018
+ */
+class ProxyStorageApiTests extends ApiRunner {
+
+    @Test
+    void correct() {
+        Optional<String> call = getApi().proxy().storageAt("0x6e03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 0);
+        assertFalse(call.isPresent());
+    }
+
+    @Test
+    void invalidParamWithError() {
+        assertThrows(EtherScanInvalidAddressException.class,
+                () -> getApi().proxy().storageAt("0xe03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 0));
+    }
+
+    @Test
+    void correctParamWithEmptyExpectedResult() {
+        final Optional<String> call = getApi().proxy().storageAt("0x6e03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 10000);
+        assertFalse(call.isPresent());
+    }
+}
diff --git a/src/test/java/io/api/etherscan/proxy/ProxyTxApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxApiTests.java
similarity index 70%
rename from src/test/java/io/api/etherscan/proxy/ProxyTxApiTest.java
rename to src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxApiTests.java
index 2779120..b20369e 100644
--- a/src/test/java/io/api/etherscan/proxy/ProxyTxApiTest.java
+++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxApiTests.java
@@ -1,22 +1,19 @@
-package io.api.etherscan.proxy;
-
-import io.api.ApiRunner;
-import io.api.etherscan.error.InvalidTxHashException;
-import io.api.etherscan.model.proxy.TxProxy;
-import org.junit.Test;
+package io.goodforgod.api.etherscan.proxy;
 
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.error.EtherScanInvalidTxHashException;
+import io.goodforgod.api.etherscan.model.proxy.TxProxy;
 import java.util.Optional;
+import org.junit.jupiter.api.Test;
 
 /**
- * ! NO DESCRIPTION !
- *
  * @author GoodforGod
  * @since 03.11.2018
  */
-public class ProxyTxApiTest extends ApiRunner {
+class ProxyTxApiTests extends ApiRunner {
 
     @Test
-    public void correctByHash() {
+    void correctByHash() {
         Optional<TxProxy> tx = getApi().proxy().tx("0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1");
         assertTrue(tx.isPresent());
         assertNotNull(tx.get().getBlockHash());
@@ -27,13 +24,13 @@ public void correctByHash() {
         assertNotNull(tx.get().getBlockNumber());
         assertNotNull(tx.get().toString());
 
-        TxProxy empty = new TxProxy();
+        TxProxy empty = TxProxy.builder().build();
         assertNotEquals(tx.get(), empty);
         assertNotEquals(tx.get().hashCode(), empty.hashCode());
     }
 
     @Test
-    public void correctByBlockNo() {
+    void correctByBlockNo() {
         Optional<TxProxy> tx = getApi().proxy().tx(637368, 0);
         assertTrue(tx.isPresent());
         assertNotNull(tx.get().getBlockHash());
@@ -52,19 +49,20 @@ public void correctByBlockNo() {
         assertNotNull(tx.get().getInput());
     }
 
-    @Test(expected = InvalidTxHashException.class)
-    public void invalidParamWithError() {
-        Optional<TxProxy> tx = getApi().proxy().tx("0xe2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1");
+    @Test
+    void invalidParamWithError() {
+        assertThrows(EtherScanInvalidTxHashException.class,
+                () -> getApi().proxy().tx("0xe2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"));
     }
 
     @Test
-    public void correctParamWithEmptyExpectedResultBlockNoExist() {
+    void correctParamWithEmptyExpectedResultBlockNoExist() {
         Optional<TxProxy> tx = getApi().proxy().tx(99999999L, 0);
         assertFalse(tx.isPresent());
     }
 
     @Test
-    public void correctParamWithEmptyExpectedResult() {
+    void correctParamWithEmptyExpectedResult() {
         Optional<TxProxy> tx = getApi().proxy().tx("0x2e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1");
         assertFalse(tx.isPresent());
     }
diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxCountApiTests.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxCountApiTests.java
new file mode 100644
index 0000000..95ed859
--- /dev/null
+++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxCountApiTests.java
@@ -0,0 +1,42 @@
+package io.goodforgod.api.etherscan.proxy;
+
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author GoodforGod
+ * @since 03.11.2018
+ */
+class ProxyTxCountApiTests extends ApiRunner {
+
+    @Test
+    void correctSended() {
+        int count = getApi().proxy().txSendCount("0x2910543af39aba0cd09dbb2d50200b3e800a63d2");
+        assertNotEquals(0, count);
+    }
+
+    @Test
+    void correctByBlockNo() {
+        int count = getApi().proxy().txCount(6137420);
+        assertNotEquals(0, count);
+    }
+
+    @Test
+    void invalidParamWithError() {
+        assertThrows(EtherScanInvalidAddressException.class,
+                () -> getApi().proxy().txSendCount("0xe03d9cce9d60f3e9f2597e13cd4c54c55330cfd"));
+    }
+
+    @Test
+    void correctParamWithEmptyExpectedResultBlockNoExist() {
+        int count = getApi().proxy().txCount(99999999999L);
+        assertNotEquals(1, count);
+    }
+
+    @Test
+    void correctParamWithEmptyExpectedResult() {
+        int count = getApi().proxy().txSendCount("0x1e03d9cce9d60f3e9f2597e13cd4c54c55330cfd");
+        assertNotEquals(1, count);
+    }
+}
diff --git a/src/test/java/io/api/etherscan/proxy/ProxyTxReceiptApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxReceiptApiTests.java
similarity index 68%
rename from src/test/java/io/api/etherscan/proxy/ProxyTxReceiptApiTest.java
rename to src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxReceiptApiTests.java
index c4a3383..662fec2 100644
--- a/src/test/java/io/api/etherscan/proxy/ProxyTxReceiptApiTest.java
+++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxReceiptApiTests.java
@@ -1,22 +1,19 @@
-package io.api.etherscan.proxy;
-
-import io.api.ApiRunner;
-import io.api.etherscan.error.InvalidTxHashException;
-import io.api.etherscan.model.proxy.ReceiptProxy;
-import org.junit.Test;
+package io.goodforgod.api.etherscan.proxy;
 
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.error.EtherScanInvalidTxHashException;
+import io.goodforgod.api.etherscan.model.proxy.ReceiptProxy;
 import java.util.Optional;
+import org.junit.jupiter.api.Test;
 
 /**
- * ! NO DESCRIPTION !
- *
  * @author GoodforGod
  * @since 03.11.2018
  */
-public class ProxyTxReceiptApiTest extends ApiRunner {
+class ProxyTxReceiptApiTests extends ApiRunner {
 
     @Test
-    public void correct() {
+    void correct() {
         Optional<ReceiptProxy> infoProxy = getApi().proxy()
                 .txReceipt("0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1");
         assertTrue(infoProxy.isPresent());
@@ -29,25 +26,25 @@ public void correct() {
         assertNotNull(infoProxy.get().getTransactionHash());
         assertNotNull(infoProxy.get().getTransactionIndex());
         assertNotNull(infoProxy.get().getGasUsed());
-        assertNotNull(infoProxy.get().getCumulativeGasUsed());
+        assertNotNull(infoProxy.get().getGasUsedCumulative());
         assertNotNull(infoProxy.get().getLogs());
         assertNotNull(infoProxy.get().getLogsBloom());
         assertNull(infoProxy.get().getContractAddress());
         assertNotNull(infoProxy.get().toString());
 
-        ReceiptProxy empty = new ReceiptProxy();
+        ReceiptProxy empty = ReceiptProxy.builder().build();
         assertNotEquals(empty, infoProxy.get());
         assertNotEquals(empty.hashCode(), infoProxy.get().hashCode());
     }
 
-    @Test(expected = InvalidTxHashException.class)
-    public void invalidParamWithError() {
-        Optional<ReceiptProxy> infoProxy = getApi().proxy()
-                .txReceipt("0xe2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1");
+    @Test
+    void invalidParamWithError() {
+        assertThrows(EtherScanInvalidTxHashException.class, () -> getApi().proxy()
+                .txReceipt("0xe2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"));
     }
 
     @Test
-    public void correctParamWithEmptyExpectedResult() {
+    void correctParamWithEmptyExpectedResult() {
         Optional<ReceiptProxy> infoProxy = getApi().proxy()
                 .txReceipt("0x2e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1");
         assertFalse(infoProxy.isPresent());
diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxSendRawApiTests.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxSendRawApiTests.java
new file mode 100644
index 0000000..3910bf8
--- /dev/null
+++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxSendRawApiTests.java
@@ -0,0 +1,36 @@
+package io.goodforgod.api.etherscan.proxy;
+
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.error.EtherScanInvalidDataHexException;
+import io.goodforgod.api.etherscan.error.EtherScanResponseException;
+import java.util.Optional;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author GoodforGod
+ * @since 03.11.2018
+ */
+// TODO contact etherscan and ask about method behavior
+class ProxyTxSendRawApiTests extends ApiRunner {
+
+    void correct() {
+        Optional<String> sendRaw = getApi().proxy()
+                .txSendRaw("0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1");
+        assertTrue(sendRaw.isPresent());
+    }
+
+    @Test
+    void invalidParamWithError() {
+        assertThrows(EtherScanInvalidDataHexException.class, () -> getApi().proxy().txSendRaw("5151=0561"));
+    }
+
+    @Test
+    void invalidParamEtherScanDataException() {
+        assertThrows(EtherScanResponseException.class, () -> getApi().proxy().txSendRaw("0x1"));
+    }
+
+    void correctParamWithEmptyExpectedResult() {
+        Optional<String> sendRaw = getApi().proxy().txSendRaw("0x000000");
+        assertFalse(sendRaw.isPresent());
+    }
+}
diff --git a/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTests.java b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTests.java
new file mode 100644
index 0000000..76b87d5
--- /dev/null
+++ b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTests.java
@@ -0,0 +1,27 @@
+package io.goodforgod.api.etherscan.statistic;
+
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.model.Price;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author GoodforGod
+ * @since 03.11.2018
+ */
+class StatisticPriceApiTests extends ApiRunner {
+
+    @Test
+    void correct() {
+        Price price = getApi().stats().priceLast();
+        assertNotNull(price);
+        assertNotNull(price.timestampBtc());
+        assertNotNull(price.timestampUsd());
+        assertNotEquals(0.0, price.inBtc().doubleValue());
+        assertNotEquals(0.0, price.inUsd().doubleValue());
+        assertNotNull(price.toString());
+
+        Price empty = Price.builder().build();
+        assertNotEquals(price, empty);
+        assertNotEquals(price.hashCode(), empty.hashCode());
+    }
+}
diff --git a/src/test/java/io/api/etherscan/statistic/StatisticSupplyApiTest.java b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyApiTests.java
similarity index 53%
rename from src/test/java/io/api/etherscan/statistic/StatisticSupplyApiTest.java
rename to src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyApiTests.java
index a705a31..6564c93 100644
--- a/src/test/java/io/api/etherscan/statistic/StatisticSupplyApiTest.java
+++ b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyApiTests.java
@@ -1,31 +1,28 @@
-package io.api.etherscan.statistic;
-
-import io.api.ApiRunner;
-import io.api.etherscan.model.Supply;
-import org.junit.Test;
+package io.goodforgod.api.etherscan.statistic;
 
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.model.Wei;
 import java.math.BigInteger;
+import org.junit.jupiter.api.Test;
 
 /**
- * ! NO DESCRIPTION !
- *
  * @author GoodforGod
  * @since 03.11.2018
  */
-public class StatisticSupplyApiTest extends ApiRunner {
+class StatisticSupplyApiTests extends ApiRunner {
 
     @Test
-    public void correct() {
-        Supply supply = getApi().stats().supply();
+    void correct() {
+        Wei supply = getApi().stats().supply();
         assertNotNull(supply);
-        assertNotNull(supply.getValue());
+        assertNotNull(supply.asWei());
         assertNotNull(supply.asGwei());
         assertNotNull(supply.asKwei());
         assertNotNull(supply.asMwei());
         assertNotNull(supply.asEther());
         assertNotNull(supply.toString());
 
-        Supply empty = new Supply(BigInteger.ONE);
+        Wei empty = Wei.ofWei(BigInteger.ONE);
         assertNotEquals(supply, empty);
         assertNotEquals(supply.hashCode(), empty.hashCode());
     }
diff --git a/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyTotalApiTests.java b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyTotalApiTests.java
new file mode 100644
index 0000000..b6098d8
--- /dev/null
+++ b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyTotalApiTests.java
@@ -0,0 +1,28 @@
+package io.goodforgod.api.etherscan.statistic;
+
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.model.EthSupply;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author GoodforGod
+ * @since 14.05.2023
+ */
+class StatisticSupplyTotalApiTests extends ApiRunner {
+
+    @Test
+    void correct() {
+        EthSupply supply = getApi().stats().supplyTotal();
+        assertNotNull(supply);
+        assertNotNull(supply.getBurntFees());
+        assertNotEquals(0, supply.getBurntFees().asWei().intValue());
+        assertNotNull(supply.getEthSupply());
+        assertNotEquals(0, supply.getEthSupply().asWei().intValue());
+        assertNotNull(supply.getEth2Staking());
+        assertNotEquals(0, supply.getEth2Staking().asWei().intValue());
+        assertNotNull(supply.getWithdrawnTotal());
+        assertNotEquals(0, supply.getWithdrawnTotal().asWei().intValue());
+        assertNotNull(supply.getTotal());
+        assertNotEquals(0, supply.getTotal().asWei().intValue());
+    }
+}
diff --git a/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticTokenSupplyApiTests.java b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticTokenSupplyApiTests.java
new file mode 100644
index 0000000..6eff846
--- /dev/null
+++ b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticTokenSupplyApiTests.java
@@ -0,0 +1,34 @@
+package io.goodforgod.api.etherscan.statistic;
+
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException;
+import io.goodforgod.api.etherscan.model.Wei;
+import java.math.BigInteger;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author GoodforGod
+ * @since 03.11.2018
+ */
+class StatisticTokenSupplyApiTests extends ApiRunner {
+
+    @Test
+    void correct() {
+        Wei supply = getApi().stats().supply("0x57d90b64a1a57749b0f932f1a3395792e12e7055");
+        assertNotNull(supply);
+        assertNotEquals(BigInteger.ZERO, supply.asWei());
+    }
+
+    @Test
+    void invalidParamWithError() {
+        assertThrows(EtherScanInvalidAddressException.class,
+                () -> getApi().stats().supply("0x7d90b64a1a57749b0f932f1a3395792e12e7055"));
+    }
+
+    @Test
+    void correctParamWithEmptyExpectedResult() {
+        Wei supply = getApi().stats().supply("0x51d90b64a1a57749b0f932f1a3395792e12e7055");
+        assertNotNull(supply);
+        assertEquals(0, supply.asEther().intValue());
+    }
+}
diff --git a/src/test/java/io/api/support/AddressUtil.java b/src/test/java/io/goodforgod/api/etherscan/support/AddressUtil.java
similarity index 95%
rename from src/test/java/io/api/support/AddressUtil.java
rename to src/test/java/io/goodforgod/api/etherscan/support/AddressUtil.java
index 7949b9e..fa007db 100644
--- a/src/test/java/io/api/support/AddressUtil.java
+++ b/src/test/java/io/goodforgod/api/etherscan/support/AddressUtil.java
@@ -1,18 +1,16 @@
-package io.api.support;
+package io.goodforgod.api.etherscan.support;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.ThreadLocalRandom;
 
 /**
- * ! NO DESCRIPTION !
- *
  * @author GoodforGod
  * @since 03.11.2018
  */
 public class AddressUtil {
 
-    public static List<String> genFakeAddresses(int size) {
+    static List<String> genFakeAddresses(int size) {
         final List<String> addresses = new ArrayList<>();
         for (int i = 0; i < size; i++)
             addresses.add("0x9327cb34984c" + ThreadLocalRandom.current().nextInt(1000, 9999) + "ec1EA0eAE98Ccf80A74f95B9");
diff --git a/src/test/java/io/goodforgod/api/etherscan/transaction/TransactionExecApiTests.java b/src/test/java/io/goodforgod/api/etherscan/transaction/TransactionExecApiTests.java
new file mode 100644
index 0000000..23e512c
--- /dev/null
+++ b/src/test/java/io/goodforgod/api/etherscan/transaction/TransactionExecApiTests.java
@@ -0,0 +1,40 @@
+package io.goodforgod.api.etherscan.transaction;
+
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.error.EtherScanInvalidTxHashException;
+import io.goodforgod.api.etherscan.model.Status;
+import java.util.Optional;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author GoodforGod
+ * @since 03.11.2018
+ */
+class TransactionExecApiTests extends ApiRunner {
+
+    @Test
+    void correct() {
+        Optional<Status> status = getApi().txs().statusExec("0x15f8e5ea1079d9a0bb04a4c58ae5fe7654b5b2b4463375ff7ffb490aa0032f3a");
+        assertTrue(status.isPresent());
+        assertTrue(status.get().haveError());
+        assertNotNull(status.get().getErrDescription());
+        assertNotNull(status.get().toString());
+
+        Status empty = Status.builder().build();
+        assertNotEquals(empty, status.get());
+        assertNotEquals(empty.hashCode(), status.get().hashCode());
+    }
+
+    @Test
+    void invalidParamWithError() {
+        assertThrows(EtherScanInvalidTxHashException.class,
+                () -> getApi().txs().statusExec("0xb513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b"));
+    }
+
+    @Test
+    void correctParamWithEmptyExpectedResult() {
+        Optional<Status> status = getApi().txs().statusExec("0x55f8e5ea1079d9a0bb04a4c58ae5fe7654b5b2b4463375ff7ffb490aa0032f3a");
+        assertTrue(status.isPresent());
+        assertFalse(status.get().haveError());
+    }
+}
diff --git a/src/test/java/io/goodforgod/api/etherscan/transaction/TransactionReceiptApiTests.java b/src/test/java/io/goodforgod/api/etherscan/transaction/TransactionReceiptApiTests.java
new file mode 100644
index 0000000..8ff0817
--- /dev/null
+++ b/src/test/java/io/goodforgod/api/etherscan/transaction/TransactionReceiptApiTests.java
@@ -0,0 +1,34 @@
+package io.goodforgod.api.etherscan.transaction;
+
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.error.EtherScanInvalidTxHashException;
+import java.util.Optional;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author GoodforGod
+ * @since 03.11.2018
+ */
+class TransactionReceiptApiTests extends ApiRunner {
+
+    @Test
+    void correct() {
+        Optional<Boolean> status = getApi().txs()
+                .statusReceipt("0x513c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76");
+        assertTrue(status.isPresent());
+        assertTrue(status.get());
+    }
+
+    @Test
+    void invalidParamWithError() {
+        assertThrows(EtherScanInvalidTxHashException.class,
+                () -> getApi().txs().statusReceipt("0x13c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76"));
+    }
+
+    @Test
+    void correctParamWithEmptyExpectedResult() {
+        Optional<Boolean> status = getApi().txs()
+                .statusReceipt("0x113c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76");
+        assertFalse(status.isPresent());
+    }
+}
diff --git a/src/test/java/io/goodforgod/api/etherscan/util/BasicUtilsTests.java b/src/test/java/io/goodforgod/api/etherscan/util/BasicUtilsTests.java
new file mode 100644
index 0000000..90a2933
--- /dev/null
+++ b/src/test/java/io/goodforgod/api/etherscan/util/BasicUtilsTests.java
@@ -0,0 +1,88 @@
+package io.goodforgod.api.etherscan.util;
+
+import static io.goodforgod.api.etherscan.util.BasicUtils.*;
+
+import com.google.gson.Gson;
+import io.goodforgod.api.etherscan.ApiRunner;
+import io.goodforgod.api.etherscan.error.EtherScanResponseException;
+import io.goodforgod.api.etherscan.model.response.StringResponseTO;
+import java.util.ArrayList;
+import java.util.List;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author GoodforGod
+ * @since 13.11.2018
+ */
+class BasicUtilsTests extends ApiRunner {
+
+    @Test
+    void responseValidateEmpty() {
+        String response = "{\"status\":\"0\",\"message\":\"No ether\",\"result\":\"status\"}";
+        StringResponseTO responseTO = new Gson().fromJson(response, StringResponseTO.class);
+
+        assertThrows(EtherScanResponseException.class, () -> validateTxResponse(responseTO));
+    }
+
+    @Test
+    void partitionEmpty() {
+        ArrayList<String> list = new ArrayList<>();
+        List<List<String>> lists = partition(list, 12);
+        assertTrue(lists.isEmpty());
+    }
+
+    @Test
+    void partitionNullParam() {
+        List<List<String>> lists = partition(null, 12);
+        assertTrue(lists.isEmpty());
+    }
+
+    @Test
+    void isBlankNull() {
+        boolean result = isBlank(null);
+        assertTrue(result);
+    }
+
+    @Test
+    void isEmptyCollectionEmpty() {
+        ArrayList<Object> list = new ArrayList<>();
+        boolean result = isEmpty(list);
+        assertTrue(result);
+    }
+
+    @Test
+    void isNotAddressNull() {
+        boolean result = isNotAddress("");
+        assertTrue(result);
+    }
+
+    @Test
+    void isNotHexNull() {
+        boolean result = isNotHex("");
+        assertTrue(result);
+    }
+
+    @Test
+    void isNotAddressInvalid() {
+        boolean result = isNotAddress("125125");
+        assertTrue(result);
+    }
+
+    @Test
+    void isNotHexInvalid() {
+        boolean result = isNotHex("1215%");
+        assertTrue(result);
+    }
+
+    @Test
+    void isResponseStatusInvalidThrows() {
+        StringResponseTO responseTO = new StringResponseTO();
+        assertThrows(EtherScanResponseException.class, () -> validateTxResponse(responseTO));
+    }
+
+    @Test
+    void isResponseNullThrows() {
+        StringResponseTO responseTO = null;
+        assertThrows(EtherScanResponseException.class, () -> validateTxResponse(responseTO));
+    }
+}