├── .editorconfig ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── publish-release.yml │ ├── publish-snapshot.yml │ └── pull-request.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── build.gradle ├── config └── codestyle.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle └── src ├── main └── java │ └── io │ └── goodforgod │ └── api │ └── etherscan │ ├── AccountAPI.java │ ├── AccountAPIProvider.java │ ├── BasicProvider.java │ ├── BlockAPI.java │ ├── BlockAPIProvider.java │ ├── ContractAPI.java │ ├── ContractAPIProvider.java │ ├── Converter.java │ ├── EthNetwork.java │ ├── EthNetworks.java │ ├── EthScanAPIBuilder.java │ ├── EtherScanAPI.java │ ├── EtherScanAPIProvider.java │ ├── GasTrackerAPI.java │ ├── GasTrackerAPIProvider.java │ ├── LogsAPI.java │ ├── LogsAPIProvider.java │ ├── ProxyAPI.java │ ├── ProxyAPIProvider.java │ ├── StatisticAPI.java │ ├── StatisticAPIProvider.java │ ├── TransactionAPI.java │ ├── TransactionAPIProvider.java │ ├── error │ ├── EtherScanConnectionException.java │ ├── EtherScanException.java │ ├── EtherScanInvalidAddressException.java │ ├── EtherScanInvalidDataHexException.java │ ├── EtherScanInvalidTxHashException.java │ ├── EtherScanKeyException.java │ ├── EtherScanLogQueryException.java │ ├── EtherScanParseException.java │ ├── EtherScanRateLimitException.java │ ├── EtherScanResponseException.java │ └── EtherScanTimeoutException.java │ ├── http │ ├── EthHttpClient.java │ └── impl │ │ └── UrlEthHttpClient.java │ ├── manager │ ├── RequestQueueManager.java │ └── impl │ │ ├── FakeRequestQueueManager.java │ │ └── SemaphoreRequestQueueManager.java │ ├── model │ ├── Abi.java │ ├── Balance.java │ ├── BaseTx.java │ ├── Block.java │ ├── BlockTx.java │ ├── BlockUncle.java │ ├── ContractCreation.java │ ├── EthSupply.java │ ├── GasOracle.java │ ├── Log.java │ ├── Price.java │ ├── Status.java │ ├── TokenBalance.java │ ├── Tx.java │ ├── TxErc1155.java │ ├── TxErc20.java │ ├── TxErc721.java │ ├── TxInternal.java │ ├── Wei.java │ ├── proxy │ │ ├── BlockProxy.java │ │ ├── ReceiptProxy.java │ │ ├── TxProxy.java │ │ └── utility │ │ │ ├── BaseProxyTO.java │ │ │ ├── BlockProxyTO.java │ │ │ ├── ErrorProxyTO.java │ │ │ ├── StringProxyTO.java │ │ │ ├── TxInfoProxyTO.java │ │ │ └── TxProxyTO.java │ ├── query │ │ ├── LogOp.java │ │ ├── LogQuery.java │ │ ├── LogQueryBuilderImpl.java │ │ ├── LogQueryImpl.java │ │ ├── LogQueryParams.java │ │ ├── LogTopicBuilder.java │ │ ├── LogTopicQuadro.java │ │ ├── LogTopicSingle.java │ │ ├── LogTopicTriple.java │ │ └── LogTopicTuple.java │ └── response │ │ ├── BalanceResponseTO.java │ │ ├── BalanceTO.java │ │ ├── BaseListResponseTO.java │ │ ├── BaseResponseTO.java │ │ ├── BlockParam.java │ │ ├── BlockResponseTO.java │ │ ├── ContractCreationResponseTO.java │ │ ├── ContractCreationTO.java │ │ ├── EthSupplyResponseTO.java │ │ ├── GasEstimateResponseTO.java │ │ ├── GasOracleResponseTO.java │ │ ├── LogResponseTO.java │ │ ├── PriceResponseTO.java │ │ ├── ReceiptStatusResponseTO.java │ │ ├── ReceiptStatusTO.java │ │ ├── StatusResponseTO.java │ │ ├── StringResponseTO.java │ │ ├── TxErc1155ResponseTO.java │ │ ├── TxErc20ResponseTO.java │ │ ├── TxErc721ResponseTO.java │ │ ├── TxInternalResponseTO.java │ │ ├── TxResponseTO.java │ │ └── UncleBlockResponseTO.java │ └── util │ └── BasicUtils.java └── test └── java └── io └── goodforgod └── api └── etherscan ├── ApiRunner.java ├── EtherScanAPITests.java ├── account ├── AccountBalanceListTests.java ├── AccountBalanceTests.java ├── AccountMinedBlocksTests.java ├── AccountTokenBalanceTests.java ├── AccountTxErc20Tests.java ├── AccountTxInternalByHashTests.java ├── AccountTxInternalTests.java ├── AccountTxRc1155TokenTests.java ├── AccountTxRc721TokenTests.java └── AccountTxsTests.java ├── block └── BlockApiTests.java ├── contract └── ContractApiTests.java ├── gastracker └── GasTrackerApiTests.java ├── logs ├── LogQueryBuilderTests.java └── LogsApiTests.java ├── manager └── SemaphoreRequestQueueManagerTests.java ├── model └── ModelBuilderTests.java ├── proxy ├── ProxyBlockApiTests.java ├── ProxyBlockLastNoApiTests.java ├── ProxyBlockUncleApiTests.java ├── ProxyCallApiTests.java ├── ProxyCodeApiTests.java ├── ProxyGasApiTests.java ├── ProxyStorageApiTests.java ├── ProxyTxApiTests.java ├── ProxyTxCountApiTests.java ├── ProxyTxReceiptApiTests.java └── ProxyTxSendRawApiTests.java ├── statistic ├── StatisticPriceApiTests.java ├── StatisticSupplyApiTests.java ├── StatisticSupplyTotalApiTests.java └── StatisticTokenSupplyApiTests.java ├── support └── AddressUtil.java ├── transaction ├── TransactionExecApiTests.java └── TransactionReceiptApiTests.java └── util └── BasicUtilsTests.java /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # all-encompassing default settings unless otherwise specified 7 | [*] 8 | end_of_line = lf 9 | charset = utf-8 10 | 11 | # Json 12 | [*.json] 13 | indent_size = 2 14 | indent_style = space 15 | insert_final_newline = false 16 | trim_trailing_whitespace = true 17 | 18 | # Yaml 19 | [{*.yml, *.yaml}] 20 | indent_size = 2 21 | indent_style = space 22 | insert_final_newline = true 23 | trim_trailing_whitespace = true 24 | 25 | # Property files 26 | [*.properties] 27 | indent_size = 2 28 | indent_style = space 29 | insert_final_newline = true 30 | trim_trailing_whitespace = true 31 | 32 | # XML files 33 | [*.xml] 34 | indent_size = 4 35 | indent_style = space 36 | insert_final_newline = true 37 | trim_trailing_whitespace = true 38 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Handle line endings automatically for files detected as text 2 | # and leave all files detected as binary untouched. 3 | * text=auto 4 | 5 | 6 | # The above will handle all files NOT found below 7 | # These files are text and should be normalized (Convert crlf => lf) 8 | *.bash text eol=lf 9 | *.css text diff=css 10 | *.df text 11 | *.htm text diff=html 12 | *.html text diff=html eol=lf 13 | *.java text diff=java eol=lf 14 | *.js text 15 | *.json text eol=lf 16 | *.jsp text eol=lf 17 | *.jspf text eol=lf 18 | *.jspx text eol=lf 19 | *.properties text eol=lf 20 | *.sh text eol=lf 21 | *.tld text 22 | *.txt text eol=lf 23 | *.tag text 24 | *.tagx text 25 | *.xml text 26 | *.yml text eol=lf 27 | 28 | 29 | # These files are binary and should be left untouched 30 | # (binary is a macro for -text -diff) 31 | # Archives 32 | *.7z binary 33 | *.br binary 34 | *.gz binary 35 | *.tar binary 36 | *.zip binary 37 | *.jar binary 38 | *.so binary 39 | *.war binary 40 | *.dll binary 41 | 42 | # Documents 43 | *.pdf binary 44 | 45 | # Images 46 | *.ico binary 47 | *.gif binary 48 | *.jpg binary 49 | *.jpeg binary 50 | *.png binary 51 | *.psd binary 52 | *.webp binary 53 | 54 | # Fonts 55 | *.woff2 binary 56 | 57 | # Other 58 | *.exe binary 59 | *.class binary 60 | *.ear binary 61 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: GoodforGod 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Additional context** 27 | Add any other context about the problem here. 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: GoodforGod 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/publish-release.yml: -------------------------------------------------------------------------------- 1 | name: CI Master 2 | 3 | on: 4 | release: 5 | types: [ published ] 6 | 7 | jobs: 8 | publish-release: 9 | runs-on: ubuntu-latest 10 | name: Publish Release 11 | 12 | steps: 13 | - uses: actions/checkout@v3 14 | - name: Set up JDK 15 | uses: actions/setup-java@v3 16 | with: 17 | java-version: '17' 18 | distribution: 'adopt' 19 | 20 | - name: Build 21 | run: './gradlew classes' 22 | 23 | - name: Test 24 | run: './gradlew test jacocoTestReport' 25 | env: 26 | ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY_2 }} 27 | 28 | - name: SonarQube 29 | run: './gradlew sonar --info' 30 | env: 31 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 32 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 33 | 34 | - name: Publish Release to GitHub Packages 35 | run: './gradlew publishMavenJavaPublicationToGitHubPackagesRepository' 36 | env: 37 | RELEASE_VERSION: ${{ github.ref_name }} 38 | GITHUB_TOKEN: ${{ secrets.OSS_GITHUB_TOKEN }} 39 | ORG_GRADLE_PROJECT_signingKey: ${{ secrets.OSS_SIGNING_KEY }} 40 | ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.OSS_SIGNING_PASSWORD }} 41 | 42 | - name: Publish Release to OSSRH 43 | run: './gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository' 44 | env: 45 | RELEASE_VERSION: ${{ github.ref_name }} 46 | OSS_USERNAME: ${{ secrets.OSS_USERNAME }} 47 | OSS_PASSWORD: ${{ secrets.OSS_PASSWORD }} 48 | ORG_GRADLE_PROJECT_signingKey: ${{ secrets.OSS_SIGNING_KEY }} 49 | ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.OSS_SIGNING_PASSWORD }} 50 | -------------------------------------------------------------------------------- /.github/workflows/publish-snapshot.yml: -------------------------------------------------------------------------------- 1 | name: CI Dev 2 | 3 | on: 4 | push: 5 | paths: 6 | - '**/workflows/*.yml' 7 | - '**/java/**' 8 | - '*.java' 9 | - '*.gradle' 10 | - '*.properties' 11 | branches: 12 | - dev 13 | 14 | jobs: 15 | publish-snapshot: 16 | runs-on: ubuntu-latest 17 | name: Publish Snapshot 18 | 19 | steps: 20 | - uses: actions/checkout@v3 21 | - name: Set up JDK 22 | uses: actions/setup-java@v3 23 | with: 24 | java-version: '17' 25 | distribution: 'adopt' 26 | 27 | - name: Code Style 28 | run: './gradlew spotlessCheck' 29 | 30 | - name: Build 31 | run: './gradlew classes' 32 | 33 | - name: Test 34 | run: './gradlew test jacocoTestReport' 35 | env: 36 | ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY_2 }} 37 | 38 | - name: Publish Snapshot 39 | run: './gradlew publish' 40 | env: 41 | OSS_USERNAME: ${{ secrets.OSS_USERNAME }} 42 | OSS_PASSWORD: ${{ secrets.OSS_PASSWORD }} 43 | ORG_GRADLE_PROJECT_signingKey: ${{ secrets.OSS_SIGNING_KEY }} 44 | ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.OSS_SIGNING_PASSWORD }} 45 | -------------------------------------------------------------------------------- /.github/workflows/pull-request.yml: -------------------------------------------------------------------------------- 1 | name: CI Pull Request 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | - dev 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | java: [ '11', '17' ] 15 | name: Java ${{ matrix.java }} Pull Request setup 16 | 17 | steps: 18 | - uses: actions/checkout@v3 19 | - name: Set up JDK 20 | uses: actions/setup-java@v3 21 | with: 22 | java-version: ${{ matrix.java }} 23 | distribution: 'adopt' 24 | 25 | - name: Code Style 26 | run: './gradlew spotlessCheck' 27 | 28 | - name: Build 29 | run: './gradlew classes' 30 | 31 | - name: Test 32 | if: matrix.java == '11' 33 | run: './gradlew test jacocoTestReport' 34 | env: 35 | ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY_1 }} 36 | 37 | - name: Test 38 | if: matrix.java == '17' 39 | run: './gradlew test jacocoTestReport' 40 | env: 41 | ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY_2 }} 42 | 43 | - name: Test Report 44 | if: matrix.java == '17' 45 | uses: EnricoMi/publish-unit-test-result-action@v2 46 | with: 47 | files: | 48 | **/test-results/**/*.xml 49 | 50 | - name: SonarQube 51 | if: matrix.java == '17' 52 | run: './gradlew sonar --info' 53 | env: 54 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 55 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Package Files 2 | *.war 3 | *.nar 4 | *.ear 5 | *.zip 6 | *.tar.gz 7 | *.rar 8 | 9 | ### Gradle template 10 | .gradle 11 | build/ 12 | target/ 13 | 14 | ### Idea generated files 15 | .idea 16 | .settings/ 17 | *.iml 18 | out/ 19 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Code or Documentation Guide 2 | 3 | ## Running Tests 4 | 5 | The new code should contain tests that check new behavior. 6 | 7 | Run tests `./gradlew test` to check that code works as behavior. 8 | 9 | ## Code Style 10 | 11 | The code base should remain clean, following industry best practices for organization, javadoc and style, as much as possible. 12 | 13 | To run the Code Style check use `./gradlew spotlessCheck`. 14 | 15 | If check found any errors, you can apply Code Style by running `./gradlew spotlessApply` 16 | 17 | ## Creating a pull request 18 | 19 | Once you are satisfied with your changes: 20 | 21 | - Commit changes to the local branch you created. 22 | - Push that branch with changes to the corresponding remote branch on GitHub 23 | - Submit a [pull request](https://help.github.com/articles/creating-a-pull-request) to `dev` branch. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 GoodforGod 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "jacoco" 3 | id "java-library" 4 | id "maven-publish" 5 | 6 | id "org.sonarqube" version "4.3.0.3225" 7 | id "com.diffplug.spotless" version "6.19.0" 8 | id "io.github.gradle-nexus.publish-plugin" version "1.3.0" 9 | } 10 | 11 | repositories { 12 | mavenLocal() 13 | mavenCentral() 14 | } 15 | 16 | group = groupId 17 | var ver = System.getenv().getOrDefault("RELEASE_VERSION", artifactVersion) 18 | version = ver.startsWith("v") ? ver.substring(1) : ver 19 | 20 | sourceCompatibility = JavaVersion.VERSION_1_8 21 | targetCompatibility = JavaVersion.VERSION_1_8 22 | 23 | dependencies { 24 | compileOnly "org.jetbrains:annotations:23.0.0" 25 | implementation "io.goodforgod:gson-configuration:2.0.0" 26 | 27 | testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.9.3" 28 | testImplementation "org.junit.jupiter:junit-jupiter-api:5.9.3" 29 | testImplementation "org.junit.jupiter:junit-jupiter-params:5.9.3" 30 | } 31 | 32 | test { 33 | failFast(false) 34 | useJUnitPlatform() 35 | testLogging { 36 | events("passed", "skipped", "failed") 37 | exceptionFormat("full") 38 | showStandardStreams(false) 39 | } 40 | 41 | reports { 42 | html.required = false 43 | junitXml.required = true 44 | } 45 | 46 | environment([ 47 | "": "", 48 | ]) 49 | } 50 | 51 | spotless { 52 | java { 53 | encoding("UTF-8") 54 | importOrder() 55 | removeUnusedImports() 56 | eclipse("4.21").configFile("${rootDir}/config/codestyle.xml") 57 | } 58 | } 59 | 60 | sonarqube { 61 | properties { 62 | property "sonar.host.url", "https://sonarcloud.io" 63 | property "sonar.organization", "goodforgod" 64 | property "sonar.projectKey", "GoodforGod_$artifactId" 65 | } 66 | } 67 | 68 | nexusPublishing { 69 | packageGroup = groupId 70 | repositories { 71 | sonatype { 72 | username = System.getenv("OSS_USERNAME") 73 | password = System.getenv("OSS_PASSWORD") 74 | nexusUrl.set(uri("https://oss.sonatype.org/service/local/")) 75 | snapshotRepositoryUrl.set(uri("https://oss.sonatype.org/content/repositories/snapshots/")) 76 | } 77 | } 78 | } 79 | 80 | publishing { 81 | publications { 82 | mavenJava(MavenPublication) { 83 | from components.java 84 | 85 | pom { 86 | name = "Java Etherscan API" 87 | url = "https://github.com/GoodforGod/$artifactId" 88 | description = "Library is a wrapper for EtherScan API." 89 | 90 | license { 91 | name = "MIT License" 92 | url = "https://github.com/GoodforGod/$artifactId/blob/master/LICENSE" 93 | distribution = "repo" 94 | } 95 | 96 | developer { 97 | id = "GoodforGod" 98 | name = "Anton Kurako" 99 | email = "goodforgod.dev@gmail.com" 100 | url = "https://github.com/GoodforGod" 101 | } 102 | 103 | scm { 104 | connection = "scm:git:git://github.com/GoodforGod/${artifactId}.git" 105 | developerConnection = "scm:git:ssh://GoodforGod/${artifactId}.git" 106 | url = "https://github.com/GoodforGod/$artifactId/tree/master" 107 | } 108 | } 109 | } 110 | } 111 | repositories { 112 | maven { 113 | def releasesRepoUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2" 114 | def snapshotsRepoUrl = "https://oss.sonatype.org/content/repositories/snapshots/" 115 | url = version.endsWith("SNAPSHOT") ? snapshotsRepoUrl : releasesRepoUrl 116 | credentials { 117 | username System.getenv("OSS_USERNAME") 118 | password System.getenv("OSS_PASSWORD") 119 | } 120 | } 121 | if (!version.endsWith("SNAPSHOT")) { 122 | maven { 123 | name = "GitHubPackages" 124 | url = "https://maven.pkg.github.com/GoodforGod/$artifactId" 125 | credentials { 126 | username = System.getenv("GITHUB_ACTOR") 127 | password = System.getenv("GITHUB_TOKEN") 128 | } 129 | } 130 | } 131 | } 132 | } 133 | 134 | java { 135 | withJavadocJar() 136 | withSourcesJar() 137 | } 138 | 139 | tasks.withType(JavaCompile) { 140 | options.encoding("UTF-8") 141 | options.incremental(true) 142 | options.fork = true 143 | } 144 | 145 | check.dependsOn jacocoTestReport 146 | jacocoTestReport { 147 | reports { 148 | xml.required = true 149 | html.destination file("${buildDir}/jacocoHtml") 150 | } 151 | } 152 | 153 | javadoc { 154 | options.encoding = "UTF-8" 155 | if (JavaVersion.current().isJava9Compatible()) { 156 | options.addBooleanOption("html5", true) 157 | } 158 | } 159 | 160 | if (project.hasProperty("signingKey")) { 161 | apply plugin: "signing" 162 | signing { 163 | def signingKey = findProperty("signingKey") 164 | def signingPassword = findProperty("signingPassword") 165 | useInMemoryPgpKeys(signingKey, signingPassword) 166 | sign publishing.publications.mavenJava 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | groupId=com.github.goodforgod 2 | artifactId=java-etherscan-api 3 | artifactVersion=2.1.0-SNAPSHOT 4 | 5 | 6 | ##### GRADLE ##### 7 | org.gradle.daemon=true 8 | org.gradle.parallel=true 9 | org.gradle.configureondemand=true 10 | org.gradle.caching=true 11 | org.gradle.jvmargs=-Dfile.encoding=UTF-8 \ 12 | --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \ 13 | --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \ 14 | --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \ 15 | --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ 16 | --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED 17 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/GoodforGod/java-etherscan-api/3de7b242b9f65daaaedbec0ef67ea83fd45c0706/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | @rem This is normally unused 30 | set APP_BASE_NAME=%~n0 31 | set APP_HOME=%DIRNAME% 32 | 33 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 34 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 35 | 36 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 37 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 38 | 39 | @rem Find java.exe 40 | if defined JAVA_HOME goto findJavaFromJavaHome 41 | 42 | set JAVA_EXE=java.exe 43 | %JAVA_EXE% -version >NUL 2>&1 44 | if %ERRORLEVEL% equ 0 goto execute 45 | 46 | echo. 47 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 48 | echo. 49 | echo Please set the JAVA_HOME variable in your environment to match the 50 | echo location of your Java installation. 51 | 52 | goto fail 53 | 54 | :findJavaFromJavaHome 55 | set JAVA_HOME=%JAVA_HOME:"=% 56 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 57 | 58 | if exist "%JAVA_EXE%" goto execute 59 | 60 | echo. 61 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 62 | echo. 63 | echo Please set the JAVA_HOME variable in your environment to match the 64 | echo location of your Java installation. 65 | 66 | goto fail 67 | 68 | :execute 69 | @rem Setup the command line 70 | 71 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 72 | 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if %ERRORLEVEL% equ 0 goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | set EXIT_CODE=%ERRORLEVEL% 85 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 86 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 87 | exit /b %EXIT_CODE% 88 | 89 | :mainEnd 90 | if "%OS%"=="Windows_NT" endlocal 91 | 92 | :omega 93 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = artifactId 2 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/BasicProvider.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan; 2 | 3 | import io.goodforgod.api.etherscan.error.EtherScanParseException; 4 | import io.goodforgod.api.etherscan.error.EtherScanRateLimitException; 5 | import io.goodforgod.api.etherscan.error.EtherScanResponseException; 6 | import io.goodforgod.api.etherscan.http.EthHttpClient; 7 | import io.goodforgod.api.etherscan.manager.RequestQueueManager; 8 | import io.goodforgod.api.etherscan.model.response.StringResponseTO; 9 | import java.net.URI; 10 | import java.nio.charset.StandardCharsets; 11 | 12 | /** 13 | * Base provider for API Implementations 14 | * 15 | * @author GoodforGod 16 | * @see EtherScanAPIProvider 17 | * @since 28.10.2018 18 | */ 19 | abstract class BasicProvider { 20 | 21 | private static final String MAX_RATE_LIMIT_REACHED = "Max rate limit reached"; 22 | 23 | static final int MAX_END_BLOCK = Integer.MAX_VALUE; 24 | static final int MIN_START_BLOCK = 0; 25 | 26 | static final String ACT_PREFIX = "&action="; 27 | 28 | private final String module; 29 | private final String baseUrl; 30 | private final EthHttpClient executor; 31 | private final RequestQueueManager queue; 32 | private final Converter converter; 33 | private final int retryCountLimit; 34 | 35 | BasicProvider(RequestQueueManager requestQueueManager, 36 | String module, 37 | String baseUrl, 38 | EthHttpClient ethHttpClient, 39 | Converter converter, 40 | int retryCountLimit) { 41 | this.queue = requestQueueManager; 42 | this.module = "&module=" + module; 43 | this.baseUrl = baseUrl; 44 | this.executor = ethHttpClient; 45 | this.converter = converter; 46 | this.retryCountLimit = retryCountLimit; 47 | } 48 | 49 | private T convert(byte[] json, Class tClass) { 50 | try { 51 | final T t = converter.fromJson(json, tClass); 52 | if (t instanceof StringResponseTO && ((StringResponseTO) t).getResult().startsWith(MAX_RATE_LIMIT_REACHED)) { 53 | throw new EtherScanRateLimitException(((StringResponseTO) t).getResult()); 54 | } 55 | 56 | return t; 57 | } catch (Exception e) { 58 | final StringResponseTO response = converter.fromJson(json, StringResponseTO.class); 59 | if (response.getResult() != null && response.getStatus() == 0) { 60 | if (response.getResult().startsWith(MAX_RATE_LIMIT_REACHED)) { 61 | throw new EtherScanRateLimitException(response.getResult()); 62 | } else { 63 | throw new EtherScanResponseException(response); 64 | } 65 | } 66 | 67 | final String jsonAsString = new String(json, StandardCharsets.UTF_8); 68 | throw new EtherScanParseException(e.getMessage() + ", for response: " + jsonAsString, e.getCause(), jsonAsString); 69 | } 70 | } 71 | 72 | private byte[] getRequest(String urlParameters) { 73 | queue.takeTurn(); 74 | final URI uri = URI.create(baseUrl + module + urlParameters); 75 | return executor.get(uri); 76 | } 77 | 78 | private byte[] postRequest(String urlParameters, String dataToPost) { 79 | queue.takeTurn(); 80 | final URI uri = URI.create(baseUrl + module + urlParameters); 81 | return executor.post(uri, dataToPost.getBytes(StandardCharsets.UTF_8)); 82 | } 83 | 84 | T getRequest(String urlParameters, Class tClass) { 85 | return getRequest(urlParameters, tClass, 0); 86 | } 87 | 88 | private T getRequest(String urlParameters, Class tClass, int retryCount) { 89 | try { 90 | return convert(getRequest(urlParameters), tClass); 91 | } catch (Exception e) { 92 | if (retryCount < retryCountLimit) { 93 | try { 94 | Thread.sleep(1150); 95 | } catch (InterruptedException ex) { 96 | throw new IllegalStateException(ex); 97 | } 98 | 99 | return getRequest(urlParameters, tClass, retryCount + 1); 100 | } else { 101 | throw e; 102 | } 103 | } 104 | } 105 | 106 | T postRequest(String urlParameters, String dataToPost, Class tClass) { 107 | return postRequest(urlParameters, dataToPost, tClass, 0); 108 | } 109 | 110 | private T postRequest(String urlParameters, String dataToPost, Class tClass, int retryCount) { 111 | try { 112 | return convert(postRequest(urlParameters, dataToPost), tClass); 113 | } catch (EtherScanRateLimitException e) { 114 | if (retryCount < retryCountLimit) { 115 | try { 116 | Thread.sleep(1150); 117 | } catch (InterruptedException ex) { 118 | throw new IllegalStateException(ex); 119 | } 120 | 121 | return postRequest(urlParameters, dataToPost, tClass, retryCount + 1); 122 | } else { 123 | throw e; 124 | } 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/BlockAPI.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan; 2 | 3 | import io.goodforgod.api.etherscan.error.EtherScanException; 4 | import io.goodforgod.api.etherscan.model.BlockUncle; 5 | import java.util.Optional; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | /** 9 | * EtherScan - API Descriptions ... 10 | * 11 | * @author GoodforGod 12 | * @since 30.10.2018 13 | */ 14 | public interface BlockAPI { 15 | 16 | /** 17 | * Return uncle blocks 18 | * 19 | * @param blockNumber block number form 0 to last 20 | * @return optional uncle blocks 21 | * @throws EtherScanException parent exception class 22 | */ 23 | @NotNull 24 | Optional uncles(long blockNumber) throws EtherScanException; 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan; 2 | 3 | import io.goodforgod.api.etherscan.error.EtherScanException; 4 | import io.goodforgod.api.etherscan.error.EtherScanResponseException; 5 | import io.goodforgod.api.etherscan.http.EthHttpClient; 6 | import io.goodforgod.api.etherscan.manager.RequestQueueManager; 7 | import io.goodforgod.api.etherscan.model.BlockUncle; 8 | import io.goodforgod.api.etherscan.model.response.UncleBlockResponseTO; 9 | import io.goodforgod.api.etherscan.util.BasicUtils; 10 | import java.util.Optional; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | /** 14 | * Block API Implementation 15 | * 16 | * @author GoodforGod 17 | * @see BlockAPI 18 | * @since 28.10.2018 19 | */ 20 | final class BlockAPIProvider extends BasicProvider implements BlockAPI { 21 | 22 | private static final String ACT_BLOCK_PARAM = ACT_PREFIX + "getblockreward"; 23 | 24 | private static final String BLOCKNO_PARAM = "&blockno="; 25 | 26 | BlockAPIProvider(RequestQueueManager requestQueueManager, 27 | String baseUrl, 28 | EthHttpClient executor, 29 | Converter converter, 30 | int retryCount) { 31 | super(requestQueueManager, "block", baseUrl, executor, converter, retryCount); 32 | } 33 | 34 | @NotNull 35 | @Override 36 | public Optional uncles(long blockNumber) throws EtherScanException { 37 | final String urlParam = ACT_BLOCK_PARAM + BLOCKNO_PARAM + blockNumber; 38 | try { 39 | final UncleBlockResponseTO responseTO = getRequest(urlParam, UncleBlockResponseTO.class); 40 | if (responseTO.getMessage().startsWith("NOTOK")) { 41 | return Optional.empty(); 42 | } 43 | 44 | BasicUtils.validateTxResponse(responseTO); 45 | return (responseTO.getResult() == null || responseTO.getResult().isEmpty()) 46 | ? Optional.empty() 47 | : Optional.of(responseTO.getResult()); 48 | } catch (EtherScanResponseException e) { 49 | if (e.getResponse().getMessage().startsWith("NOTOK")) { 50 | return Optional.empty(); 51 | } else { 52 | throw e; 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/ContractAPI.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan; 2 | 3 | import io.goodforgod.api.etherscan.error.EtherScanException; 4 | import io.goodforgod.api.etherscan.model.Abi; 5 | import io.goodforgod.api.etherscan.model.ContractCreation; 6 | import java.util.List; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | /** 10 | * EtherScan - API Descriptions ... 11 | * 12 | * @author GoodforGod 13 | * @since 28.10.2018 14 | */ 15 | public interface ContractAPI { 16 | 17 | /** 18 | * Get Verified Contract Sources 19 | * 20 | * @param address to verify 21 | * @return ABI verified 22 | * @throws EtherScanException parent exception class 23 | */ 24 | @NotNull 25 | Abi contractAbi(@NotNull String address) throws EtherScanException; 26 | 27 | /** 28 | * Returns a contract's deployer address and transaction hash it was created, up to 5 at a time. 29 | * 30 | * @param contractAddresses - list of addresses to fetch 31 | * @throws EtherScanException parent exception class 32 | */ 33 | @NotNull 34 | List contractCreation(@NotNull List contractAddresses) throws EtherScanException; 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan; 2 | 3 | import io.goodforgod.api.etherscan.error.EtherScanException; 4 | import io.goodforgod.api.etherscan.error.EtherScanResponseException; 5 | import io.goodforgod.api.etherscan.http.EthHttpClient; 6 | import io.goodforgod.api.etherscan.manager.RequestQueueManager; 7 | import io.goodforgod.api.etherscan.model.Abi; 8 | import io.goodforgod.api.etherscan.model.ContractCreation; 9 | import io.goodforgod.api.etherscan.model.response.ContractCreationResponseTO; 10 | import io.goodforgod.api.etherscan.model.response.StringResponseTO; 11 | import io.goodforgod.api.etherscan.util.BasicUtils; 12 | import java.util.List; 13 | import java.util.stream.Collectors; 14 | import org.jetbrains.annotations.NotNull; 15 | 16 | /** 17 | * Contract API Implementation 18 | * 19 | * @see ContractAPI 20 | * @author GoodforGod 21 | * @since 28.10.2018 22 | */ 23 | final class ContractAPIProvider extends BasicProvider implements ContractAPI { 24 | 25 | private static final String ACT_ABI_PARAM = ACT_PREFIX + "getabi"; 26 | 27 | private static final String ADDRESS_PARAM = "&address="; 28 | 29 | private static final String ACT_CONTRACT_CREATION_PARAM = "getcontractcreation"; 30 | 31 | private static final String ACT_CONTRACT_CREATION = ACT_PREFIX + ACT_CONTRACT_CREATION_PARAM; 32 | 33 | private static final String ACT_CONTRACT_ADDRESSES_PARAM = "&contractaddresses="; 34 | 35 | ContractAPIProvider(RequestQueueManager requestQueueManager, 36 | String baseUrl, 37 | EthHttpClient executor, 38 | Converter converter, 39 | int retryCount) { 40 | super(requestQueueManager, "contract", baseUrl, executor, converter, retryCount); 41 | } 42 | 43 | @NotNull 44 | @Override 45 | public Abi contractAbi(@NotNull String address) throws EtherScanException { 46 | BasicUtils.validateAddress(address); 47 | 48 | final String urlParam = ACT_ABI_PARAM + ADDRESS_PARAM + address; 49 | final StringResponseTO response = getRequest(urlParam, StringResponseTO.class); 50 | if (response.getStatus() != 1 && response.getMessage().startsWith("NOTOK")) { 51 | throw new EtherScanResponseException(response); 52 | } 53 | 54 | return (response.getResult().startsWith("Contract sou")) 55 | ? Abi.nonVerified() 56 | : Abi.verified(response.getResult()); 57 | } 58 | 59 | @NotNull 60 | @Override 61 | public List contractCreation(@NotNull List contractAddresses) throws EtherScanException { 62 | BasicUtils.validateAddresses(contractAddresses); 63 | final String urlParam = ACT_CONTRACT_CREATION + ACT_CONTRACT_ADDRESSES_PARAM 64 | + BasicUtils.toAddressParam(contractAddresses); 65 | final ContractCreationResponseTO response = getRequest(urlParam, ContractCreationResponseTO.class); 66 | if (response.getStatus() != 1 && response.getMessage().startsWith("NOTOK")) { 67 | throw new EtherScanResponseException(response); 68 | } 69 | 70 | return response.getResult().stream() 71 | .map(to -> ContractCreation.builder() 72 | .withContractCreator(to.getContractCreator()) 73 | .withContractAddress(to.getContractAddress()) 74 | .withTxHash(to.getTxHash()) 75 | .build()) 76 | .collect(Collectors.toList()); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/Converter.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | /** 6 | * @author Anton Kurako (GoodforGod) 7 | * @since 14.05.2023 8 | */ 9 | public interface Converter { 10 | 11 | @NotNull 12 | T fromJson(byte[] jsonAsByteArray, @NotNull Class type); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/EthNetwork.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan; 2 | 3 | import java.net.URI; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | /** 7 | * @author Anton Kurako (GoodforGod) 8 | * @since 11.05.2023 9 | */ 10 | public interface EthNetwork { 11 | 12 | /** 13 | * @return URI for network domain like EtherScan API 14 | */ 15 | @NotNull 16 | URI domain(); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/EthNetworks.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan; 2 | 3 | import java.net.URI; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | /** 7 | * @author GoodforGod 8 | * @since 28.10.2018 9 | */ 10 | public enum EthNetworks implements EthNetwork { 11 | 12 | MAINNET("api", "io"), 13 | GORLI("api-goerli", "io"), 14 | SEPOLIA("api-sepolia", "io"); 15 | 16 | private final URI domain; 17 | 18 | EthNetworks(String domain, String extension) { 19 | this.domain = URI.create("https://" + domain + ".etherscan." + extension + "/api"); 20 | } 21 | 22 | @Override 23 | public @NotNull URI domain() { 24 | return domain; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan; 2 | 3 | import com.google.gson.Gson; 4 | import io.goodforgod.api.etherscan.error.EtherScanKeyException; 5 | import io.goodforgod.api.etherscan.error.EtherScanParseException; 6 | import io.goodforgod.api.etherscan.http.EthHttpClient; 7 | import io.goodforgod.api.etherscan.http.impl.UrlEthHttpClient; 8 | import io.goodforgod.api.etherscan.manager.RequestQueueManager; 9 | import io.goodforgod.api.etherscan.util.BasicUtils; 10 | import io.goodforgod.gson.configuration.GsonConfiguration; 11 | import java.io.ByteArrayInputStream; 12 | import java.io.IOException; 13 | import java.io.InputStreamReader; 14 | import java.nio.charset.StandardCharsets; 15 | import java.util.function.Supplier; 16 | import org.jetbrains.annotations.NotNull; 17 | 18 | /** 19 | * @author Anton Kurako (GoodforGod) 20 | * @since 11.05.2023 21 | */ 22 | final class EthScanAPIBuilder implements EtherScanAPI.Builder { 23 | 24 | private static final Supplier DEFAULT_SUPPLIER = UrlEthHttpClient::new; 25 | private static final String DEFAULT_KEY = "YourApiKeyToken"; 26 | 27 | private final Gson gson = new GsonConfiguration().builder().create(); 28 | 29 | private int retryCountOnLimitReach = 0; 30 | private String apiKey = DEFAULT_KEY; 31 | private RequestQueueManager queueManager; 32 | private EthNetwork ethNetwork = EthNetworks.MAINNET; 33 | private Supplier ethHttpClientSupplier = DEFAULT_SUPPLIER; 34 | private Supplier converterSupplier = () -> new Converter() { 35 | 36 | @Override 37 | public @NotNull T fromJson(byte[] jsonAsByteArray, @NotNull Class type) { 38 | try (InputStreamReader isr = new InputStreamReader(new ByteArrayInputStream(jsonAsByteArray))) { 39 | return gson.fromJson(isr, type); 40 | } catch (IOException e) { 41 | throw new EtherScanParseException(e.getMessage(), e, new String(jsonAsByteArray, StandardCharsets.UTF_8)); 42 | } 43 | } 44 | }; 45 | 46 | @NotNull 47 | @Override 48 | public EtherScanAPI.Builder withApiKey(@NotNull String apiKey) { 49 | if (BasicUtils.isBlank(apiKey)) 50 | throw new EtherScanKeyException("API key can not be null or empty"); 51 | 52 | this.apiKey = apiKey; 53 | return this; 54 | } 55 | 56 | @NotNull 57 | @Override 58 | public EtherScanAPI.Builder withNetwork(@NotNull EthNetwork network) { 59 | this.ethNetwork = network; 60 | return this; 61 | } 62 | 63 | @NotNull 64 | @Override 65 | public EtherScanAPI.Builder withNetwork(@NotNull EthNetworks network) { 66 | this.ethNetwork = network; 67 | return this; 68 | } 69 | 70 | @NotNull 71 | @Override 72 | public EtherScanAPI.Builder withQueue(@NotNull RequestQueueManager queueManager) { 73 | this.queueManager = queueManager; 74 | return this; 75 | } 76 | 77 | @NotNull 78 | @Override 79 | public EtherScanAPI.Builder withHttpClient(@NotNull Supplier httpClientSupplier) { 80 | this.ethHttpClientSupplier = httpClientSupplier; 81 | return this; 82 | } 83 | 84 | @NotNull 85 | @Override 86 | public EtherScanAPI.Builder withConverter(@NotNull Supplier converterSupplier) { 87 | this.converterSupplier = converterSupplier; 88 | return this; 89 | } 90 | 91 | @NotNull 92 | public EtherScanAPI.Builder withRetryOnRateLimit(int maxRetryCount) { 93 | if (maxRetryCount < 0 || maxRetryCount > 20) { 94 | throw new IllegalStateException("maxRetryCount value must be in range from 0 to 20, but was: " + maxRetryCount); 95 | } 96 | 97 | this.retryCountOnLimitReach = maxRetryCount; 98 | return this; 99 | } 100 | 101 | @Override 102 | public @NotNull EtherScanAPI build() { 103 | RequestQueueManager requestQueueManager; 104 | if (queueManager != null) { 105 | requestQueueManager = queueManager; 106 | } else if (DEFAULT_KEY.equals(apiKey)) { 107 | requestQueueManager = RequestQueueManager.anonymous(); 108 | } else { 109 | requestQueueManager = RequestQueueManager.planFree(); 110 | } 111 | 112 | return new EtherScanAPIProvider(apiKey, ethNetwork, requestQueueManager, ethHttpClientSupplier.get(), 113 | converterSupplier.get(), retryCountOnLimitReach); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan; 2 | 3 | import io.goodforgod.api.etherscan.error.EtherScanRateLimitException; 4 | import io.goodforgod.api.etherscan.http.EthHttpClient; 5 | import io.goodforgod.api.etherscan.manager.RequestQueueManager; 6 | import java.util.function.Supplier; 7 | import org.jetbrains.annotations.NotNull; 8 | import org.jetbrains.annotations.Range; 9 | 10 | /** 11 | * EtherScan full API Description ... 12 | * 13 | * @author GoodforGod 14 | * @since 10.05.2023 15 | */ 16 | public interface EtherScanAPI extends AutoCloseable { 17 | 18 | @NotNull 19 | AccountAPI account(); 20 | 21 | @NotNull 22 | ContractAPI contract(); 23 | 24 | @NotNull 25 | TransactionAPI txs(); 26 | 27 | @NotNull 28 | BlockAPI block(); 29 | 30 | @NotNull 31 | LogsAPI logs(); 32 | 33 | @NotNull 34 | ProxyAPI proxy(); 35 | 36 | @NotNull 37 | StatisticAPI stats(); 38 | 39 | @NotNull 40 | GasTrackerAPI gasTracker(); 41 | 42 | @NotNull 43 | static Builder builder() { 44 | return new EthScanAPIBuilder(); 45 | } 46 | 47 | interface Builder { 48 | 49 | @NotNull 50 | Builder withApiKey(@NotNull String apiKey); 51 | 52 | @NotNull 53 | Builder withNetwork(@NotNull EthNetwork network); 54 | 55 | @NotNull 56 | Builder withNetwork(@NotNull EthNetworks network); 57 | 58 | @NotNull 59 | Builder withQueue(@NotNull RequestQueueManager queueManager); 60 | 61 | @NotNull 62 | Builder withHttpClient(@NotNull Supplier httpClientSupplier); 63 | 64 | @NotNull 65 | Builder withConverter(@NotNull Supplier converterSupplier); 66 | 67 | /** 68 | * By default is disabled 69 | * 70 | * @param maxRetryCount to retry if {@link EtherScanRateLimitException} thrown 71 | * @return self 72 | */ 73 | @NotNull 74 | EtherScanAPI.Builder withRetryOnRateLimit(@Range(from = 0, to = 20) int maxRetryCount); 75 | 76 | @NotNull 77 | EtherScanAPI build(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/EtherScanAPIProvider.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan; 2 | 3 | import io.goodforgod.api.etherscan.http.EthHttpClient; 4 | import io.goodforgod.api.etherscan.manager.RequestQueueManager; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | /** 8 | * EtherScan full API Description ... 9 | * 10 | * @author GoodforGod 11 | * @since 28.10.2018 12 | */ 13 | final class EtherScanAPIProvider implements EtherScanAPI { 14 | 15 | private final RequestQueueManager requestQueueManager; 16 | private final AccountAPI account; 17 | private final BlockAPI block; 18 | private final ContractAPI contract; 19 | private final LogsAPI logs; 20 | private final ProxyAPI proxy; 21 | private final StatisticAPI stats; 22 | private final TransactionAPI txs; 23 | private final GasTrackerAPI gasTracker; 24 | 25 | EtherScanAPIProvider(String apiKey, 26 | EthNetwork network, 27 | RequestQueueManager queue, 28 | EthHttpClient ethHttpClient, 29 | Converter converter, 30 | int retryCount) { 31 | // EtherScan 1request\5sec limit support by queue manager 32 | final String baseUrl = network.domain() + "?apikey=" + apiKey; 33 | 34 | this.requestQueueManager = queue; 35 | this.account = new AccountAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount); 36 | this.block = new BlockAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount); 37 | this.contract = new ContractAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount); 38 | this.logs = new LogsAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount); 39 | this.proxy = new ProxyAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount); 40 | this.stats = new StatisticAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount); 41 | this.txs = new TransactionAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount); 42 | this.gasTracker = new GasTrackerAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount); 43 | } 44 | 45 | @NotNull 46 | @Override 47 | public AccountAPI account() { 48 | return account; 49 | } 50 | 51 | @NotNull 52 | @Override 53 | public ContractAPI contract() { 54 | return contract; 55 | } 56 | 57 | @NotNull 58 | @Override 59 | public TransactionAPI txs() { 60 | return txs; 61 | } 62 | 63 | @NotNull 64 | @Override 65 | public BlockAPI block() { 66 | return block; 67 | } 68 | 69 | @NotNull 70 | @Override 71 | public LogsAPI logs() { 72 | return logs; 73 | } 74 | 75 | @NotNull 76 | @Override 77 | public ProxyAPI proxy() { 78 | return proxy; 79 | } 80 | 81 | @NotNull 82 | @Override 83 | public StatisticAPI stats() { 84 | return stats; 85 | } 86 | 87 | @Override 88 | public @NotNull GasTrackerAPI gasTracker() { 89 | return gasTracker; 90 | } 91 | 92 | @Override 93 | public void close() throws Exception { 94 | requestQueueManager.close(); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/GasTrackerAPI.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan; 2 | 3 | import io.goodforgod.api.etherscan.error.EtherScanException; 4 | import io.goodforgod.api.etherscan.model.GasOracle; 5 | import io.goodforgod.api.etherscan.model.Wei; 6 | import java.time.Duration; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | /** 10 | * EtherScan - API Descriptions 11 | * ... 12 | * 13 | * @author Abhay Gupta 14 | * @since 14.11.2022 15 | */ 16 | public interface GasTrackerAPI { 17 | 18 | /** 19 | * Returns the estimated time for a transaction to be confirmed on the blockchain. 20 | * 21 | * @return estimated time 22 | * @throws EtherScanException parent exception class 23 | */ 24 | @NotNull 25 | Duration estimate(@NotNull Wei wei) throws EtherScanException; 26 | 27 | /** 28 | * Returns the current Safe, Proposed and Fast gas prices. 29 | * 30 | * @return fast, suggested gas price 31 | * @throws EtherScanException parent exception class 32 | */ 33 | @NotNull 34 | GasOracle oracle() throws EtherScanException; 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan; 2 | 3 | import io.goodforgod.api.etherscan.error.EtherScanException; 4 | import io.goodforgod.api.etherscan.error.EtherScanResponseException; 5 | import io.goodforgod.api.etherscan.http.EthHttpClient; 6 | import io.goodforgod.api.etherscan.manager.RequestQueueManager; 7 | import io.goodforgod.api.etherscan.model.GasOracle; 8 | import io.goodforgod.api.etherscan.model.Wei; 9 | import io.goodforgod.api.etherscan.model.response.GasEstimateResponseTO; 10 | import io.goodforgod.api.etherscan.model.response.GasOracleResponseTO; 11 | import java.time.Duration; 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | /** 15 | * GasTracker API Implementation 16 | * 17 | * @see GasTrackerAPI 18 | * @author Abhay Gupta 19 | * @since 14.11.2022 20 | */ 21 | final class GasTrackerAPIProvider extends BasicProvider implements GasTrackerAPI { 22 | 23 | private static final String ACT_GAS_ORACLE_PARAM = ACT_PREFIX + "gasoracle"; 24 | private static final String ACT_GAS_ESTIMATE_PARAM = ACT_PREFIX + "gasestimate"; 25 | 26 | private static final String GASPRICE_PARAM = "&gasprice="; 27 | 28 | GasTrackerAPIProvider(RequestQueueManager queue, 29 | String baseUrl, 30 | EthHttpClient ethHttpClient, 31 | Converter converter, 32 | int retryCount) { 33 | super(queue, "gastracker", baseUrl, ethHttpClient, converter, retryCount); 34 | } 35 | 36 | @Override 37 | public @NotNull Duration estimate(@NotNull Wei wei) throws EtherScanException { 38 | final String urlParams = ACT_GAS_ESTIMATE_PARAM + GASPRICE_PARAM + wei.asWei().toString(); 39 | final GasEstimateResponseTO response = getRequest(urlParams, GasEstimateResponseTO.class); 40 | if (response.getStatus() != 1) 41 | throw new EtherScanResponseException(response); 42 | 43 | return Duration.ofSeconds(Long.parseLong(response.getResult())); 44 | } 45 | 46 | @NotNull 47 | @Override 48 | public GasOracle oracle() throws EtherScanException { 49 | final GasOracleResponseTO response = getRequest(ACT_GAS_ORACLE_PARAM, GasOracleResponseTO.class); 50 | if (response.getStatus() != 1) 51 | throw new EtherScanResponseException(response); 52 | 53 | return response.getResult(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/LogsAPI.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan; 2 | 3 | import io.goodforgod.api.etherscan.error.EtherScanException; 4 | import io.goodforgod.api.etherscan.model.Log; 5 | import io.goodforgod.api.etherscan.model.query.LogQuery; 6 | import java.util.List; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | /** 10 | * EtherScan - API Descriptions ... 11 | * 12 | * @author GoodforGod 13 | * @since 30.10.2018 14 | */ 15 | public interface LogsAPI { 16 | 17 | /** 18 | * alternative to the native eth_getLogs Read at EtherScan API description for full info! 19 | * 20 | * @param query build log query 21 | * @return logs according to query 22 | * @throws EtherScanException parent exception class 23 | * @see LogQuery 24 | */ 25 | @NotNull 26 | List logs(@NotNull LogQuery query) throws EtherScanException; 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/LogsAPIProvider.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan; 2 | 3 | import io.goodforgod.api.etherscan.error.EtherScanException; 4 | import io.goodforgod.api.etherscan.http.EthHttpClient; 5 | import io.goodforgod.api.etherscan.manager.RequestQueueManager; 6 | import io.goodforgod.api.etherscan.model.Log; 7 | import io.goodforgod.api.etherscan.model.query.LogQuery; 8 | import io.goodforgod.api.etherscan.model.response.LogResponseTO; 9 | import io.goodforgod.api.etherscan.util.BasicUtils; 10 | import java.util.Collections; 11 | import java.util.List; 12 | import org.jetbrains.annotations.NotNull; 13 | 14 | /** 15 | * Logs API Implementation 16 | * 17 | * @see LogsAPI 18 | * @author GoodforGod 19 | * @since 28.10.2018 20 | */ 21 | final class LogsAPIProvider extends BasicProvider implements LogsAPI { 22 | 23 | private static final String ACT_LOGS_PARAM = ACT_PREFIX + "getLogs"; 24 | 25 | LogsAPIProvider(RequestQueueManager queue, 26 | String baseUrl, 27 | EthHttpClient executor, 28 | Converter converter, 29 | int retryCount) { 30 | super(queue, "logs", baseUrl, executor, converter, retryCount); 31 | } 32 | 33 | @NotNull 34 | @Override 35 | public List logs(@NotNull LogQuery query) throws EtherScanException { 36 | final String urlParams = ACT_LOGS_PARAM + query.params(); 37 | final LogResponseTO response = getRequest(urlParams, LogResponseTO.class); 38 | BasicUtils.validateTxResponse(response); 39 | 40 | return (BasicUtils.isEmpty(response.getResult())) 41 | ? Collections.emptyList() 42 | : response.getResult(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan; 2 | 3 | import io.goodforgod.api.etherscan.error.EtherScanException; 4 | import io.goodforgod.api.etherscan.model.EthSupply; 5 | import io.goodforgod.api.etherscan.model.Price; 6 | import io.goodforgod.api.etherscan.model.Wei; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | /** 10 | * EtherScan - API Descriptions ... 11 | * 12 | * @author GoodforGod 13 | * @since 30.10.2018 14 | */ 15 | public interface StatisticAPI { 16 | 17 | /** 18 | * ERC20 token total Supply 19 | * EtherScan 21 | * Returns the current amount of an ERC-20 token in circulation. 22 | * 23 | * @param contract contract address 24 | * @return token supply for specified contract 25 | * @throws EtherScanException parent exception class 26 | */ 27 | @NotNull 28 | Wei supply(@NotNull String contract) throws EtherScanException; 29 | 30 | /** 31 | * Returns the current amount of Ether in circulation excluding ETH2 Staking rewards and EIP1559 32 | * burnt fees. 33 | * 34 | * @return total ETH supply for moment 35 | * @throws EtherScanException parent exception class 36 | */ 37 | @NotNull 38 | Wei supply() throws EtherScanException; 39 | 40 | /** 41 | * Returns the current amount of Ether in circulation, ETH2 Staking rewards, EIP1559 burnt fees, and 42 | * total withdrawn ETH from the beacon chain. 43 | * 44 | * @return total ETH supply for moment 45 | * @throws EtherScanException parent exception class 46 | */ 47 | @NotNull 48 | EthSupply supplyTotal() throws EtherScanException; 49 | 50 | /** 51 | * Eth last USD and BTC price 52 | * 53 | * @return last usd/btc price for ETH 54 | * @throws EtherScanException parent exception class 55 | */ 56 | @NotNull 57 | Price priceLast() throws EtherScanException; 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan; 2 | 3 | import io.goodforgod.api.etherscan.error.EtherScanException; 4 | import io.goodforgod.api.etherscan.error.EtherScanResponseException; 5 | import io.goodforgod.api.etherscan.http.EthHttpClient; 6 | import io.goodforgod.api.etherscan.manager.RequestQueueManager; 7 | import io.goodforgod.api.etherscan.model.EthSupply; 8 | import io.goodforgod.api.etherscan.model.Price; 9 | import io.goodforgod.api.etherscan.model.Wei; 10 | import io.goodforgod.api.etherscan.model.response.EthSupplyResponseTO; 11 | import io.goodforgod.api.etherscan.model.response.PriceResponseTO; 12 | import io.goodforgod.api.etherscan.model.response.StringResponseTO; 13 | import io.goodforgod.api.etherscan.util.BasicUtils; 14 | import java.math.BigInteger; 15 | import org.jetbrains.annotations.NotNull; 16 | 17 | /** 18 | * Statistic API Implementation 19 | * 20 | * @see StatisticAPI 21 | * @author GoodforGod 22 | * @since 28.10.2018 23 | */ 24 | final class StatisticAPIProvider extends BasicProvider implements StatisticAPI { 25 | 26 | private static final String ACT_SUPPLY_PARAM = ACT_PREFIX + "ethsupply"; 27 | private static final String ACT_SUPPLY2_PARAM = ACT_PREFIX + "ethsupply2"; 28 | private static final String ACT_TOKEN_SUPPLY_PARAM = ACT_PREFIX + "tokensupply"; 29 | private static final String ACT_LASTPRICE_PARAM = ACT_PREFIX + "ethprice"; 30 | 31 | private static final String CONTRACT_ADDRESS_PARAM = "&contractaddress="; 32 | 33 | StatisticAPIProvider(RequestQueueManager queue, 34 | String baseUrl, 35 | EthHttpClient executor, 36 | Converter converter, 37 | int retry) { 38 | super(queue, "stats", baseUrl, executor, converter, retry); 39 | } 40 | 41 | @NotNull 42 | @Override 43 | public Wei supply() throws EtherScanException { 44 | final StringResponseTO response = getRequest(ACT_SUPPLY_PARAM, StringResponseTO.class); 45 | if (response.getStatus() != 1) 46 | throw new EtherScanResponseException(response); 47 | 48 | return Wei.ofWei(new BigInteger(response.getResult())); 49 | } 50 | 51 | @Override 52 | public @NotNull EthSupply supplyTotal() throws EtherScanException { 53 | final EthSupplyResponseTO response = getRequest(ACT_SUPPLY2_PARAM, EthSupplyResponseTO.class); 54 | if (response.getStatus() != 1) 55 | throw new EtherScanResponseException(response); 56 | 57 | return response.getResult(); 58 | } 59 | 60 | @NotNull 61 | @Override 62 | public Wei supply(@NotNull String contract) throws EtherScanException { 63 | BasicUtils.validateAddress(contract); 64 | 65 | final String urlParams = ACT_TOKEN_SUPPLY_PARAM + CONTRACT_ADDRESS_PARAM + contract; 66 | final StringResponseTO response = getRequest(urlParams, StringResponseTO.class); 67 | if (response.getStatus() != 1) 68 | throw new EtherScanResponseException(response); 69 | 70 | return Wei.ofWei(new BigInteger(response.getResult())); 71 | } 72 | 73 | @NotNull 74 | @Override 75 | public Price priceLast() throws EtherScanException { 76 | final PriceResponseTO response = getRequest(ACT_LASTPRICE_PARAM, PriceResponseTO.class); 77 | if (response.getStatus() != 1) 78 | throw new EtherScanResponseException(response); 79 | 80 | return response.getResult(); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/TransactionAPI.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan; 2 | 3 | import io.goodforgod.api.etherscan.error.EtherScanException; 4 | import io.goodforgod.api.etherscan.model.Status; 5 | import java.util.Optional; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | /** 9 | * EtherScan - API Descriptions ... 10 | * 11 | * @author GoodforGod 12 | * @since 30.10.2018 13 | */ 14 | public interface TransactionAPI { 15 | 16 | /** 17 | * Check Contract Execution Status (if there was an error during contract execution) 18 | * 19 | * @param txhash transaction hash 20 | * @return optional status result 21 | * @throws EtherScanException parent exception class 22 | */ 23 | @NotNull 24 | Optional statusExec(@NotNull String txhash) throws EtherScanException; 25 | 26 | /** 27 | * Check Transaction Receipt Status (Only applicable for Post Byzantium fork transactions) 28 | * 29 | * @param txhash transaction hash 30 | * @return 0 = Fail, 1 = Pass, empty value for pre-byzantium fork 31 | * @throws EtherScanException parent exception class 32 | */ 33 | @NotNull 34 | Optional statusReceipt(@NotNull String txhash) throws EtherScanException; 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/TransactionAPIProvider.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan; 2 | 3 | import io.goodforgod.api.etherscan.error.EtherScanException; 4 | import io.goodforgod.api.etherscan.http.EthHttpClient; 5 | import io.goodforgod.api.etherscan.manager.RequestQueueManager; 6 | import io.goodforgod.api.etherscan.model.Status; 7 | import io.goodforgod.api.etherscan.model.response.ReceiptStatusResponseTO; 8 | import io.goodforgod.api.etherscan.model.response.StatusResponseTO; 9 | import io.goodforgod.api.etherscan.util.BasicUtils; 10 | import java.util.Optional; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | /** 14 | * Transaction API Implementation 15 | * 16 | * @author GoodforGod 17 | * @see TransactionAPI 18 | * @since 28.10.2018 19 | */ 20 | final class TransactionAPIProvider extends BasicProvider implements TransactionAPI { 21 | 22 | private static final String ACT_EXEC_STATUS_PARAM = ACT_PREFIX + "getstatus"; 23 | private static final String ACT_RECEIPT_STATUS_PARAM = ACT_PREFIX + "gettxreceiptstatus"; 24 | 25 | private static final String TXHASH_PARAM = "&txhash="; 26 | 27 | TransactionAPIProvider(RequestQueueManager queue, 28 | String baseUrl, 29 | EthHttpClient executor, 30 | Converter converter, 31 | int retryCount) { 32 | super(queue, "transaction", baseUrl, executor, converter, retryCount); 33 | } 34 | 35 | @NotNull 36 | @Override 37 | public Optional statusExec(@NotNull String txhash) throws EtherScanException { 38 | BasicUtils.validateTxHash(txhash); 39 | 40 | final String urlParams = ACT_EXEC_STATUS_PARAM + TXHASH_PARAM + txhash; 41 | final StatusResponseTO response = getRequest(urlParams, StatusResponseTO.class); 42 | BasicUtils.validateTxResponse(response); 43 | 44 | return Optional.ofNullable(response.getResult()); 45 | } 46 | 47 | @NotNull 48 | @Override 49 | public Optional statusReceipt(@NotNull String txhash) throws EtherScanException { 50 | BasicUtils.validateTxHash(txhash); 51 | 52 | final String urlParams = ACT_RECEIPT_STATUS_PARAM + TXHASH_PARAM + txhash; 53 | final ReceiptStatusResponseTO response = getRequest(urlParams, ReceiptStatusResponseTO.class); 54 | BasicUtils.validateTxResponse(response); 55 | 56 | return (response.getResult() == null || BasicUtils.isEmpty(response.getResult().getStatus())) 57 | ? Optional.empty() 58 | : Optional.of(response.getResult().getStatus().contains("1")); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/error/EtherScanConnectionException.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.error; 2 | 3 | /** 4 | * @author GoodforGod 5 | * @since 29.10.2018 6 | */ 7 | public class EtherScanConnectionException extends EtherScanException { 8 | 9 | public EtherScanConnectionException(String message) { 10 | super(message); 11 | } 12 | 13 | public EtherScanConnectionException(String message, Throwable cause) { 14 | super(message, cause); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/error/EtherScanException.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.error; 2 | 3 | /** 4 | * @author GoodforGod 5 | * @since 30.10.2018 6 | */ 7 | public class EtherScanException extends RuntimeException { 8 | 9 | public EtherScanException(String message) { 10 | super(message); 11 | } 12 | 13 | public EtherScanException(String message, Throwable cause) { 14 | super(message, cause); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidAddressException.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.error; 2 | 3 | /** 4 | * @author GoodforGod 5 | * @since 29.10.2018 6 | */ 7 | public class EtherScanInvalidAddressException extends EtherScanException { 8 | 9 | public EtherScanInvalidAddressException(String message) { 10 | super(message); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidDataHexException.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.error; 2 | 3 | /** 4 | * @author GoodforGod 5 | * @since 02.11.2018 6 | */ 7 | public class EtherScanInvalidDataHexException extends EtherScanException { 8 | 9 | public EtherScanInvalidDataHexException(String message) { 10 | super(message); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidTxHashException.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.error; 2 | 3 | /** 4 | * @author GoodforGod 5 | * @since 02.11.2018 6 | */ 7 | public class EtherScanInvalidTxHashException extends EtherScanException { 8 | 9 | public EtherScanInvalidTxHashException(String message) { 10 | super(message); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/error/EtherScanKeyException.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.error; 2 | 3 | /** 4 | * @author GoodforGod 5 | * @since 05.11.2018 6 | */ 7 | public class EtherScanKeyException extends EtherScanException { 8 | 9 | public EtherScanKeyException(String message) { 10 | super(message); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/error/EtherScanLogQueryException.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.error; 2 | 3 | /** 4 | * @author GoodforGod 5 | * @since 31.10.2018 6 | */ 7 | public class EtherScanLogQueryException extends EtherScanException { 8 | 9 | public EtherScanLogQueryException(String message) { 10 | super(message); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/error/EtherScanParseException.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.error; 2 | 3 | /** 4 | * @author GoodforGod 5 | * @since 29.10.2018 6 | */ 7 | public class EtherScanParseException extends EtherScanException { 8 | 9 | private final String json; 10 | 11 | public EtherScanParseException(String message, Throwable cause, String json) { 12 | super(message, cause); 13 | this.json = json; 14 | } 15 | 16 | public String getJson() { 17 | return json; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/error/EtherScanRateLimitException.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.error; 2 | 3 | /** 4 | * @author iSnow 5 | * @since 2020-10-06 6 | */ 7 | public class EtherScanRateLimitException extends EtherScanException { 8 | 9 | public EtherScanRateLimitException(String message) { 10 | super(message); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/error/EtherScanResponseException.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.error; 2 | 3 | import io.goodforgod.api.etherscan.model.response.BaseResponseTO; 4 | import io.goodforgod.api.etherscan.model.response.StringResponseTO; 5 | 6 | /** 7 | * @author GoodforGod 8 | * @since 29.10.2018 9 | */ 10 | public class EtherScanResponseException extends EtherScanException { 11 | 12 | private final transient BaseResponseTO response; 13 | 14 | public EtherScanResponseException(BaseResponseTO response) { 15 | this(response, response.getMessage() + ", with status: " + response.getStatus()); 16 | } 17 | 18 | public EtherScanResponseException(StringResponseTO response) { 19 | this(response, 20 | response.getResult() + ", with status: " + response.getStatus() + ", with message: " + response.getMessage()); 21 | } 22 | 23 | public EtherScanResponseException(BaseResponseTO response, String message) { 24 | super(message); 25 | this.response = response; 26 | } 27 | 28 | public BaseResponseTO getResponse() { 29 | return response; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/error/EtherScanTimeoutException.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.error; 2 | 3 | /** 4 | * @author GoodforGod 5 | * @since 12.11.2018 6 | */ 7 | public class EtherScanTimeoutException extends EtherScanConnectionException { 8 | 9 | public EtherScanTimeoutException(String message, Throwable cause) { 10 | super(message, cause); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/http/EthHttpClient.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.http; 2 | 3 | import java.net.URI; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | /** 7 | * Http Client interface 8 | * 9 | * @author GoodforGod 10 | * @since 31.10.2018 11 | */ 12 | public interface EthHttpClient { 13 | 14 | /** 15 | * Performs a Http GET request 16 | * 17 | * @param uri as string 18 | * @return result as string 19 | */ 20 | byte[] get(@NotNull URI uri); 21 | 22 | /** 23 | * Performs a Http POST request 24 | * 25 | * @param uri as string 26 | * @param body to post 27 | * @return result as string 28 | */ 29 | byte[] post(@NotNull URI uri, byte[] body); 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.manager; 2 | 3 | import io.goodforgod.api.etherscan.manager.impl.FakeRequestQueueManager; 4 | import io.goodforgod.api.etherscan.manager.impl.SemaphoreRequestQueueManager; 5 | import java.time.Duration; 6 | 7 | /** 8 | * Queue manager to support API limits 9 | * Manager grants turn if the limit is not exhausted And resets queue each set period 10 | * 11 | * @author GoodforGod 12 | * @since 30.10.2018 13 | */ 14 | public interface RequestQueueManager extends AutoCloseable { 15 | 16 | /** 17 | * Is used by default when no API KEY is provided 18 | */ 19 | static RequestQueueManager anonymous() { 20 | return new SemaphoreRequestQueueManager(1, Duration.ofMillis(5045L)); 21 | } 22 | 23 | /** 24 | * Is available for all registered free API KEYs 25 | * Free API KEY 26 | */ 27 | static RequestQueueManager planFree() { 28 | return new SemaphoreRequestQueueManager(5, Duration.ofMillis(1045L)); 29 | } 30 | 31 | static RequestQueueManager planStandard() { 32 | return new SemaphoreRequestQueueManager(10, Duration.ofMillis(1045L)); 33 | } 34 | 35 | static RequestQueueManager planAdvanced() { 36 | return new SemaphoreRequestQueueManager(20, Duration.ofMillis(1045L)); 37 | } 38 | 39 | static RequestQueueManager planProfessional() { 40 | return new SemaphoreRequestQueueManager(30, Duration.ofMillis(1045L)); 41 | } 42 | 43 | static RequestQueueManager unlimited() { 44 | return new FakeRequestQueueManager(); 45 | } 46 | 47 | /** 48 | * Waits in queue for chance to take turn 49 | */ 50 | void takeTurn(); 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/manager/impl/FakeRequestQueueManager.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.manager.impl; 2 | 3 | import io.goodforgod.api.etherscan.manager.RequestQueueManager; 4 | 5 | /** 6 | * Fake queue manager, always give turns, when you have no limits 7 | * 8 | * @author GoodforGod 9 | * @since 03.11.2018 10 | */ 11 | public final class FakeRequestQueueManager implements RequestQueueManager { 12 | 13 | @Override 14 | public void takeTurn() { 15 | // no limit or await provided for fake impl so rate limit exception will be 16 | // thrown if too many calls 17 | } 18 | 19 | @Override 20 | public void close() { 21 | // do nothing 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/manager/impl/SemaphoreRequestQueueManager.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.manager.impl; 2 | 3 | import io.goodforgod.api.etherscan.manager.RequestQueueManager; 4 | import java.time.Duration; 5 | import java.util.concurrent.Executors; 6 | import java.util.concurrent.ScheduledExecutorService; 7 | import java.util.concurrent.Semaphore; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | /** 11 | * Queue Semaphore implementation with size and reset time as params 12 | * 13 | * @see RequestQueueManager 14 | * @author GoodforGod 15 | * @since 30.10.2018 16 | */ 17 | public final class SemaphoreRequestQueueManager implements RequestQueueManager, AutoCloseable { 18 | 19 | private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); 20 | private final Semaphore semaphore; 21 | private final long queueResetTimeInMillis; 22 | 23 | public SemaphoreRequestQueueManager(int size, Duration resetIn) { 24 | this.semaphore = new Semaphore(0); 25 | this.queueResetTimeInMillis = resetIn.toMillis(); 26 | this.executorService.scheduleAtFixedRate(releaseLocks(size), 27 | resetIn.toMillis(), queueResetTimeInMillis, TimeUnit.MILLISECONDS); 28 | } 29 | 30 | @SuppressWarnings("java:S899") 31 | @Override 32 | public void takeTurn() { 33 | try { 34 | semaphore.tryAcquire(queueResetTimeInMillis, TimeUnit.MILLISECONDS); 35 | } catch (InterruptedException e) { 36 | Thread.currentThread().interrupt(); 37 | } 38 | } 39 | 40 | private Runnable releaseLocks(int toRelease) { 41 | return () -> { 42 | int availablePermits = semaphore.availablePermits(); 43 | int neededPermits = toRelease - availablePermits; 44 | if (neededPermits > 0) { 45 | semaphore.release(neededPermits); 46 | } 47 | }; 48 | } 49 | 50 | @Override 51 | public void close() { 52 | executorService.shutdown(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/Abi.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model; 2 | 3 | import io.goodforgod.api.etherscan.util.BasicUtils; 4 | import java.util.Objects; 5 | 6 | /** 7 | * @author GoodforGod 8 | * @since 31.10.2018 9 | */ 10 | public class Abi { 11 | 12 | private final String contractAbi; 13 | private final boolean isVerified; 14 | 15 | private Abi(String contractAbi, boolean isVerified) { 16 | this.contractAbi = contractAbi; 17 | this.isVerified = isVerified; 18 | } 19 | 20 | public static Abi verified(String contractAbi) { 21 | return new Abi(contractAbi, true); 22 | } 23 | 24 | public static Abi nonVerified() { 25 | return new Abi("", false); 26 | } 27 | 28 | public boolean haveAbi() { 29 | return !BasicUtils.isEmpty(contractAbi); 30 | } 31 | 32 | public String getContractAbi() { 33 | return contractAbi; 34 | } 35 | 36 | public boolean isVerified() { 37 | return isVerified; 38 | } 39 | 40 | @Override 41 | public boolean equals(Object o) { 42 | if (this == o) 43 | return true; 44 | if (!(o instanceof Abi)) 45 | return false; 46 | Abi abi = (Abi) o; 47 | return isVerified == abi.isVerified && Objects.equals(contractAbi, abi.contractAbi); 48 | } 49 | 50 | @Override 51 | public int hashCode() { 52 | return Objects.hash(contractAbi, isVerified); 53 | } 54 | 55 | @Override 56 | public String toString() { 57 | return "Abi{" + 58 | "contractAbi=" + contractAbi + 59 | ", isVerified=" + isVerified + 60 | '}'; 61 | } 62 | 63 | public static AbiBuilder builder() { 64 | return new AbiBuilder(); 65 | } 66 | 67 | public static final class AbiBuilder { 68 | 69 | private String contractAbi; 70 | private boolean isVerified; 71 | 72 | private AbiBuilder() {} 73 | 74 | public AbiBuilder withContractAbi(String contractAbi) { 75 | this.contractAbi = contractAbi; 76 | return this; 77 | } 78 | 79 | public AbiBuilder withIsVerified(boolean isVerified) { 80 | this.isVerified = isVerified; 81 | return this; 82 | } 83 | 84 | public Abi build() { 85 | return new Abi(contractAbi, isVerified); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/Balance.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model; 2 | 3 | import java.util.Objects; 4 | 5 | /** 6 | * @author GoodforGod 7 | * @since 28.10.2018 8 | */ 9 | public class Balance { 10 | 11 | /** Balance in Wei */ 12 | private final Wei balance; 13 | private final String address; 14 | 15 | public Balance(String address, Wei balance) { 16 | this.address = address; 17 | this.balance = balance; 18 | } 19 | 20 | // 21 | public String getAddress() { 22 | return address; 23 | } 24 | 25 | public Wei getBalanceInWei() { 26 | return balance; 27 | } 28 | // 29 | 30 | @Override 31 | public boolean equals(Object o) { 32 | if (this == o) 33 | return true; 34 | if (!(o instanceof Balance)) 35 | return false; 36 | Balance balance1 = (Balance) o; 37 | return Objects.equals(balance, balance1.balance) && Objects.equals(address, balance1.address); 38 | } 39 | 40 | @Override 41 | public int hashCode() { 42 | return Objects.hash(balance, address); 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | return "Balance{" + 48 | "address=" + address + 49 | ", balance=" + balance + 50 | '}'; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/BaseTx.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import io.goodforgod.api.etherscan.util.BasicUtils; 5 | import java.math.BigInteger; 6 | import java.time.LocalDateTime; 7 | import java.time.ZoneOffset; 8 | import java.util.Objects; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | /** 12 | * @author GoodforGod 13 | * @since 28.10.2018 14 | */ 15 | abstract class BaseTx implements Comparable { 16 | 17 | long blockNumber; 18 | String timeStamp; 19 | @Expose(deserialize = false, serialize = false) 20 | LocalDateTime _timeStamp; 21 | String hash; 22 | String from; 23 | String to; 24 | String contractAddress; 25 | String input; 26 | BigInteger gas; 27 | BigInteger gasUsed; 28 | 29 | // 30 | public long getBlockNumber() { 31 | return blockNumber; 32 | } 33 | 34 | public LocalDateTime getTimeStamp() { 35 | if (_timeStamp == null && !BasicUtils.isEmpty(timeStamp)) 36 | _timeStamp = LocalDateTime.ofEpochSecond(Long.parseLong(timeStamp), 0, ZoneOffset.UTC); 37 | return _timeStamp; 38 | } 39 | 40 | public String getHash() { 41 | return hash; 42 | } 43 | 44 | public String getFrom() { 45 | return from; 46 | } 47 | 48 | public String getTo() { 49 | return to; 50 | } 51 | 52 | public String getContractAddress() { 53 | return contractAddress; 54 | } 55 | 56 | public String getInput() { 57 | return input; 58 | } 59 | 60 | public Wei getGas() { 61 | return Wei.ofWei(gas); 62 | } 63 | 64 | public Wei getGasUsed() { 65 | return Wei.ofWei(gasUsed); 66 | } 67 | // 68 | 69 | @Override 70 | public boolean equals(Object o) { 71 | if (this == o) 72 | return true; 73 | if (!(o instanceof BaseTx)) 74 | return false; 75 | BaseTx baseTx = (BaseTx) o; 76 | return blockNumber == baseTx.blockNumber && Objects.equals(timeStamp, baseTx.timeStamp) 77 | && Objects.equals(hash, baseTx.hash) && Objects.equals(from, baseTx.from) && Objects.equals(to, baseTx.to) 78 | && Objects.equals(contractAddress, baseTx.contractAddress) && Objects.equals(input, baseTx.input) 79 | && Objects.equals(gas, baseTx.gas) && Objects.equals(gasUsed, baseTx.gasUsed); 80 | } 81 | 82 | @Override 83 | public int hashCode() { 84 | return Objects.hash(blockNumber, timeStamp, hash, from, to, contractAddress, input, gas, gasUsed); 85 | } 86 | 87 | @Override 88 | public int compareTo(@NotNull BaseTx o) { 89 | return Long.compare(blockNumber, o.blockNumber); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/Block.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import io.goodforgod.api.etherscan.util.BasicUtils; 5 | import java.math.BigInteger; 6 | import java.time.LocalDateTime; 7 | import java.time.ZoneOffset; 8 | import java.util.Objects; 9 | import org.jetbrains.annotations.NotNull; 10 | 11 | /** 12 | * @author GoodforGod 13 | * @since 28.10.2018 14 | */ 15 | public class Block implements Comparable { 16 | 17 | long blockNumber; 18 | BigInteger blockReward; 19 | String timeStamp; 20 | @Expose(deserialize = false, serialize = false) 21 | LocalDateTime _timeStamp; 22 | 23 | protected Block() {} 24 | 25 | // 26 | public long getBlockNumber() { 27 | return blockNumber; 28 | } 29 | 30 | public LocalDateTime getTimeStamp() { 31 | if (_timeStamp == null && !BasicUtils.isEmpty(timeStamp)) 32 | _timeStamp = LocalDateTime.ofEpochSecond(Long.parseLong(timeStamp), 0, ZoneOffset.UTC); 33 | return _timeStamp; 34 | } 35 | 36 | public BigInteger getBlockReward() { 37 | return blockReward; 38 | } 39 | // 40 | 41 | @Override 42 | public boolean equals(Object o) { 43 | if (this == o) 44 | return true; 45 | if (!(o instanceof Block)) 46 | return false; 47 | Block block = (Block) o; 48 | return blockNumber == block.blockNumber; 49 | } 50 | 51 | @Override 52 | public int hashCode() { 53 | return Objects.hash(blockNumber); 54 | } 55 | 56 | @Override 57 | public String toString() { 58 | return "Block{" + 59 | "blockNumber=" + blockNumber + 60 | ", blockReward=" + blockReward + 61 | ", timeStamp=" + timeStamp + 62 | '}'; 63 | } 64 | 65 | @Override 66 | public int compareTo(@NotNull Block o) { 67 | return Long.compare(blockNumber, o.blockNumber); 68 | } 69 | 70 | public static BlockBuilder builder() { 71 | return new BlockBuilder(); 72 | } 73 | 74 | public static class BlockBuilder { 75 | 76 | private long blockNumber; 77 | private BigInteger blockReward; 78 | private LocalDateTime timeStamp; 79 | 80 | BlockBuilder() {} 81 | 82 | public BlockBuilder withBlockNumber(long blockNumber) { 83 | this.blockNumber = blockNumber; 84 | return this; 85 | } 86 | 87 | public BlockBuilder withBlockReward(BigInteger blockReward) { 88 | this.blockReward = blockReward; 89 | return this; 90 | } 91 | 92 | public BlockBuilder withTimeStamp(LocalDateTime timeStamp) { 93 | this.timeStamp = timeStamp; 94 | return this; 95 | } 96 | 97 | public Block build() { 98 | Block block = new Block(); 99 | block.blockNumber = this.blockNumber; 100 | block.blockReward = this.blockReward; 101 | if (this.timeStamp != null) { 102 | block._timeStamp = this.timeStamp; 103 | block.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); 104 | } 105 | return block; 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/BlockTx.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model; 2 | 3 | import java.math.BigInteger; 4 | import java.util.Objects; 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | /** 8 | * @author Anton Kurako (GoodforGod) 9 | * @since 15.05.2023 10 | */ 11 | abstract class BlockTx extends BaseTx { 12 | 13 | long nonce; 14 | String blockHash; 15 | long transactionIndex; 16 | long confirmations; 17 | BigInteger gasPrice; 18 | BigInteger cumulativeGasUsed; 19 | 20 | public long getNonce() { 21 | return nonce; 22 | } 23 | 24 | public String getBlockHash() { 25 | return blockHash; 26 | } 27 | 28 | public long getTransactionIndex() { 29 | return transactionIndex; 30 | } 31 | 32 | public Wei getGasPrice() { 33 | return Wei.ofWei(gasPrice); 34 | } 35 | 36 | public Wei getGasUsedCumulative() { 37 | return Wei.ofWei(cumulativeGasUsed); 38 | } 39 | 40 | public long getConfirmations() { 41 | return confirmations; 42 | } 43 | 44 | @Override 45 | public boolean equals(Object o) { 46 | if (this == o) 47 | return true; 48 | if (!(o instanceof BlockTx)) 49 | return false; 50 | if (!super.equals(o)) 51 | return false; 52 | BlockTx blockTx = (BlockTx) o; 53 | return nonce == blockTx.nonce && transactionIndex == blockTx.transactionIndex 54 | && Objects.equals(blockHash, blockTx.blockHash); 55 | } 56 | 57 | @Override 58 | public int hashCode() { 59 | return Objects.hash(super.hashCode(), nonce, blockHash, transactionIndex); 60 | } 61 | 62 | @Override 63 | public int compareTo(@NotNull BaseTx o) { 64 | final int superCompare = super.compareTo(o); 65 | if (superCompare == 0) { 66 | if (o instanceof Tx) { 67 | return Long.compare(transactionIndex, ((Tx) o).getTransactionIndex()); 68 | } else if (o instanceof TxErc20) { 69 | return Long.compare(transactionIndex, ((TxErc20) o).getTransactionIndex()); 70 | } else if (o instanceof TxErc721) { 71 | return Long.compare(transactionIndex, ((TxErc721) o).getTransactionIndex()); 72 | } else if (o instanceof TxErc1155) { 73 | return Long.compare(transactionIndex, ((TxErc1155) o).getTransactionIndex()); 74 | } 75 | } 76 | 77 | return superCompare; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/ContractCreation.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model; 2 | 3 | import java.util.Objects; 4 | 5 | public class ContractCreation { 6 | 7 | private String contractAddress; 8 | private String contractCreator; 9 | private String txHash; 10 | 11 | protected ContractCreation() {} 12 | 13 | public String getContractAddress() { 14 | return contractAddress; 15 | } 16 | 17 | public String getContractCreator() { 18 | return contractCreator; 19 | } 20 | 21 | public String getTxHash() { 22 | return txHash; 23 | } 24 | 25 | @Override 26 | public boolean equals(Object o) { 27 | if (this == o) 28 | return true; 29 | if (o == null || getClass() != o.getClass()) 30 | return false; 31 | ContractCreation that = (ContractCreation) o; 32 | return Objects.equals(contractAddress, that.contractAddress) 33 | && Objects.equals(contractCreator, that.contractCreator) 34 | && Objects.equals(txHash, that.txHash); 35 | } 36 | 37 | @Override 38 | public int hashCode() { 39 | return Objects.hash(contractAddress, contractCreator, txHash); 40 | } 41 | 42 | @Override 43 | public String toString() { 44 | return "ContractCreation{" + 45 | "contractAddress=" + contractAddress + 46 | ", contractCreator=" + contractCreator + 47 | ", txHash=" + txHash + 48 | '}'; 49 | } 50 | 51 | public static ContractCreationBuilder builder() { 52 | return new ContractCreationBuilder(); 53 | } 54 | 55 | public static final class ContractCreationBuilder { 56 | 57 | private String contractAddress; 58 | private String contractCreator; 59 | private String txHash; 60 | 61 | private ContractCreationBuilder() {} 62 | 63 | public ContractCreationBuilder withContractAddress(String contractAddress) { 64 | this.contractAddress = contractAddress; 65 | return this; 66 | } 67 | 68 | public ContractCreationBuilder withContractCreator(String contractCreator) { 69 | this.contractCreator = contractCreator; 70 | return this; 71 | } 72 | 73 | public ContractCreationBuilder withTxHash(String txHash) { 74 | this.txHash = txHash; 75 | return this; 76 | } 77 | 78 | public ContractCreation build() { 79 | ContractCreation contractCreation = new ContractCreation(); 80 | contractCreation.contractAddress = contractAddress; 81 | contractCreation.contractCreator = contractCreator; 82 | contractCreation.txHash = txHash; 83 | return contractCreation; 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/EthSupply.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model; 2 | 3 | import java.math.BigInteger; 4 | import java.util.Objects; 5 | 6 | /** 7 | * @author Anton Kurako (GoodforGod) 8 | * @since 14.05.2023 9 | */ 10 | public class EthSupply { 11 | 12 | private String EthSupply; 13 | private String Eth2Staking; 14 | private String BurntFees; 15 | private String WithdrawnTotal; 16 | 17 | public Wei getEthSupply() { 18 | return Wei.ofWei(new BigInteger(EthSupply)); 19 | } 20 | 21 | public Wei getEth2Staking() { 22 | return Wei.ofWei(new BigInteger(Eth2Staking)); 23 | } 24 | 25 | public Wei getBurntFees() { 26 | return Wei.ofWei(new BigInteger(BurntFees)); 27 | } 28 | 29 | public Wei getTotal() { 30 | final BigInteger total = getEthSupply().asWei() 31 | .add(getEth2Staking().asWei()) 32 | .min(getBurntFees().asWei()); 33 | return Wei.ofWei(total); 34 | } 35 | 36 | public Wei getWithdrawnTotal() { 37 | return Wei.ofWei(new BigInteger(WithdrawnTotal)); 38 | } 39 | 40 | @Override 41 | public boolean equals(Object o) { 42 | if (this == o) 43 | return true; 44 | if (!(o instanceof EthSupply)) 45 | return false; 46 | EthSupply ethSupply = (EthSupply) o; 47 | return Objects.equals(EthSupply, ethSupply.EthSupply) && Objects.equals(Eth2Staking, ethSupply.Eth2Staking) 48 | && Objects.equals(BurntFees, ethSupply.BurntFees) && Objects.equals(WithdrawnTotal, ethSupply.WithdrawnTotal); 49 | } 50 | 51 | @Override 52 | public int hashCode() { 53 | return Objects.hash(EthSupply, Eth2Staking, BurntFees, WithdrawnTotal); 54 | } 55 | 56 | @Override 57 | public String toString() { 58 | return "EthSupply{" + 59 | "EthSupply=" + EthSupply + 60 | ", Eth2Staking=" + Eth2Staking + 61 | ", BurntFees=" + BurntFees + 62 | ", WithdrawnTotal=" + WithdrawnTotal + 63 | '}'; 64 | } 65 | 66 | public static EthSupplyBuilder builder() { 67 | return new EthSupplyBuilder(); 68 | } 69 | 70 | public static final class EthSupplyBuilder { 71 | 72 | private Wei ethSupply; 73 | private Wei eth2Staking; 74 | private Wei burntFees; 75 | private Wei withdrawnTotal; 76 | 77 | private EthSupplyBuilder() {} 78 | 79 | public EthSupplyBuilder withEthSupply(Wei ethSupply) { 80 | this.ethSupply = ethSupply; 81 | return this; 82 | } 83 | 84 | public EthSupplyBuilder withEth2Staking(Wei eth2Staking) { 85 | this.eth2Staking = eth2Staking; 86 | return this; 87 | } 88 | 89 | public EthSupplyBuilder withBurntFees(Wei burntFees) { 90 | this.burntFees = burntFees; 91 | return this; 92 | } 93 | 94 | public EthSupplyBuilder withWithdrawnTotal(Wei withdrawnTotal) { 95 | this.withdrawnTotal = withdrawnTotal; 96 | return this; 97 | } 98 | 99 | public EthSupply build() { 100 | EthSupply ethSupply = new EthSupply(); 101 | if (this.burntFees != null) { 102 | ethSupply.BurntFees = this.burntFees.toString(); 103 | } else { 104 | ethSupply.BurntFees = BigInteger.ZERO.toString(); 105 | } 106 | if (this.eth2Staking != null) { 107 | ethSupply.Eth2Staking = this.eth2Staking.toString(); 108 | } else { 109 | ethSupply.Eth2Staking = BigInteger.ZERO.toString(); 110 | } 111 | if (this.ethSupply != null) { 112 | ethSupply.EthSupply = this.ethSupply.toString(); 113 | } else { 114 | ethSupply.EthSupply = BigInteger.ZERO.toString(); 115 | } 116 | if (this.withdrawnTotal != null) { 117 | ethSupply.WithdrawnTotal = this.withdrawnTotal.toString(); 118 | } else { 119 | ethSupply.WithdrawnTotal = BigInteger.ZERO.toString(); 120 | } 121 | return ethSupply; 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model; 2 | 3 | import java.math.BigDecimal; 4 | import java.math.BigInteger; 5 | import java.util.Arrays; 6 | import java.util.List; 7 | import java.util.Objects; 8 | import java.util.stream.Collectors; 9 | 10 | /** 11 | * @author Abhay Gupta 12 | * @since 14.11.2022 13 | */ 14 | public class GasOracle { 15 | 16 | private Long LastBlock; 17 | private BigInteger SafeGasPrice; 18 | private BigInteger ProposeGasPrice; 19 | private BigInteger FastGasPrice; 20 | private BigDecimal suggestBaseFee; 21 | private String gasUsedRatio; 22 | 23 | protected GasOracle() {} 24 | 25 | public Long getLastBlock() { 26 | return LastBlock; 27 | } 28 | 29 | public Wei getSafeGasPriceInWei() { 30 | return Wei.ofGwei(SafeGasPrice); 31 | } 32 | 33 | public Wei getProposeGasPriceInWei() { 34 | return Wei.ofGwei(ProposeGasPrice); 35 | } 36 | 37 | public Wei getFastGasPriceInWei() { 38 | return Wei.ofGwei(FastGasPrice); 39 | } 40 | 41 | public BigDecimal getSuggestBaseFee() { 42 | return suggestBaseFee; 43 | } 44 | 45 | public List getGasUsedRatio() { 46 | return Arrays.stream(gasUsedRatio.split(",")) 47 | .map(BigDecimal::new) 48 | .collect(Collectors.toList()); 49 | } 50 | 51 | @Override 52 | public boolean equals(Object o) { 53 | if (this == o) 54 | return true; 55 | if (!(o instanceof GasOracle)) 56 | return false; 57 | GasOracle gasOracle = (GasOracle) o; 58 | return Objects.equals(LastBlock, gasOracle.LastBlock) && Objects.equals(SafeGasPrice, gasOracle.SafeGasPrice) 59 | && Objects.equals(ProposeGasPrice, gasOracle.ProposeGasPrice) 60 | && Objects.equals(FastGasPrice, gasOracle.FastGasPrice) 61 | && Objects.equals(suggestBaseFee, gasOracle.suggestBaseFee) 62 | && Objects.equals(gasUsedRatio, gasOracle.gasUsedRatio); 63 | } 64 | 65 | @Override 66 | public int hashCode() { 67 | return Objects.hash(LastBlock, SafeGasPrice, ProposeGasPrice, FastGasPrice, suggestBaseFee, gasUsedRatio); 68 | } 69 | 70 | @Override 71 | public String toString() { 72 | return "GasOracle{" + 73 | "LastBlock=" + LastBlock + 74 | ", SafeGasPrice=" + SafeGasPrice + 75 | ", ProposeGasPrice=" + ProposeGasPrice + 76 | ", FastGasPrice=" + FastGasPrice + 77 | ", suggestBaseFee=" + suggestBaseFee + 78 | ", gasUsedRatio=" + gasUsedRatio + 79 | '}'; 80 | } 81 | 82 | public static GasOracleBuilder builder() { 83 | return new GasOracleBuilder(); 84 | } 85 | 86 | public static final class GasOracleBuilder { 87 | 88 | private Long lastBlock; 89 | private Wei safeGasPrice; 90 | private Wei proposeGasPrice; 91 | private Wei fastGasPrice; 92 | private BigDecimal suggestBaseFee; 93 | private List gasUsedRatio; 94 | 95 | private GasOracleBuilder() {} 96 | 97 | public GasOracleBuilder withLastBlock(Long lastBlock) { 98 | this.lastBlock = lastBlock; 99 | return this; 100 | } 101 | 102 | public GasOracleBuilder withSafeGasPrice(Wei safeGasPrice) { 103 | this.safeGasPrice = safeGasPrice; 104 | return this; 105 | } 106 | 107 | public GasOracleBuilder withProposeGasPrice(Wei proposeGasPrice) { 108 | this.proposeGasPrice = proposeGasPrice; 109 | return this; 110 | } 111 | 112 | public GasOracleBuilder withFastGasPrice(Wei fastGasPrice) { 113 | this.fastGasPrice = fastGasPrice; 114 | return this; 115 | } 116 | 117 | public GasOracleBuilder withSuggestBaseFee(BigDecimal suggestBaseFee) { 118 | this.suggestBaseFee = suggestBaseFee; 119 | return this; 120 | } 121 | 122 | public GasOracleBuilder withGasUsedRatio(List gasUsedRatio) { 123 | this.gasUsedRatio = gasUsedRatio; 124 | return this; 125 | } 126 | 127 | public GasOracle build() { 128 | GasOracle gasOracle = new GasOracle(); 129 | gasOracle.LastBlock = this.lastBlock; 130 | gasOracle.suggestBaseFee = this.suggestBaseFee; 131 | if (this.proposeGasPrice != null) { 132 | gasOracle.ProposeGasPrice = this.proposeGasPrice.asGwei().toBigInteger(); 133 | } 134 | if (this.safeGasPrice != null) { 135 | gasOracle.SafeGasPrice = this.safeGasPrice.asGwei().toBigInteger(); 136 | } 137 | if (this.fastGasPrice != null) { 138 | gasOracle.FastGasPrice = this.fastGasPrice.asGwei().toBigInteger(); 139 | } 140 | if (this.gasUsedRatio != null) { 141 | gasOracle.gasUsedRatio = this.gasUsedRatio.stream() 142 | .map(BigDecimal::toString) 143 | .collect(Collectors.joining(",")); 144 | } 145 | return gasOracle; 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/Price.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model; 2 | 3 | import com.google.gson.annotations.Expose; 4 | import java.math.BigDecimal; 5 | import java.time.LocalDateTime; 6 | import java.time.ZoneOffset; 7 | import java.util.Objects; 8 | 9 | /** 10 | * @author GoodforGod 11 | * @since 30.10.2018 12 | */ 13 | public class Price { 14 | 15 | private BigDecimal ethusd; 16 | private BigDecimal ethbtc; 17 | private String ethusd_timestamp; 18 | private String ethbtc_timestamp; 19 | @Expose(deserialize = false, serialize = false) 20 | private LocalDateTime _ethusd_timestamp; 21 | @Expose(deserialize = false, serialize = false) 22 | private LocalDateTime _ethbtc_timestamp; 23 | 24 | protected Price() {} 25 | 26 | public BigDecimal inUsd() { 27 | return ethusd; 28 | } 29 | 30 | public BigDecimal inBtc() { 31 | return ethbtc; 32 | } 33 | 34 | public LocalDateTime timestampUsd() { 35 | if (_ethusd_timestamp == null && ethusd_timestamp != null) { 36 | _ethusd_timestamp = LocalDateTime.ofEpochSecond(Long.parseLong(ethusd_timestamp), 0, ZoneOffset.UTC); 37 | } 38 | return _ethusd_timestamp; 39 | } 40 | 41 | public LocalDateTime timestampBtc() { 42 | if (_ethbtc_timestamp == null && ethbtc_timestamp != null) { 43 | _ethbtc_timestamp = LocalDateTime.ofEpochSecond(Long.parseLong(ethbtc_timestamp), 0, ZoneOffset.UTC); 44 | } 45 | return _ethbtc_timestamp; 46 | } 47 | 48 | @Override 49 | public boolean equals(Object o) { 50 | if (this == o) 51 | return true; 52 | if (!(o instanceof Price)) 53 | return false; 54 | Price price = (Price) o; 55 | return Objects.equals(ethusd, price.ethusd) && Objects.equals(ethbtc, price.ethbtc) 56 | && Objects.equals(ethusd_timestamp, price.ethusd_timestamp) 57 | && Objects.equals(ethbtc_timestamp, price.ethbtc_timestamp); 58 | } 59 | 60 | @Override 61 | public int hashCode() { 62 | return Objects.hash(ethusd, ethbtc, ethusd_timestamp, ethbtc_timestamp); 63 | } 64 | 65 | @Override 66 | public String toString() { 67 | return "Price{" + 68 | "ethusd=" + ethusd + 69 | ", ethbtc=" + ethbtc + 70 | ", ethusd_timestamp=" + ethusd_timestamp + 71 | ", ethbtc_timestamp=" + ethbtc_timestamp + 72 | '}'; 73 | } 74 | 75 | public static PriceBuilder builder() { 76 | return new PriceBuilder(); 77 | } 78 | 79 | public static final class PriceBuilder { 80 | 81 | private BigDecimal ethusd; 82 | private BigDecimal ethbtc; 83 | private LocalDateTime ethusdTimestamp; 84 | private LocalDateTime ethbtcTimestamp; 85 | 86 | private PriceBuilder() {} 87 | 88 | public PriceBuilder withUsd(BigDecimal ethToUsd) { 89 | this.ethusd = ethToUsd; 90 | return this; 91 | } 92 | 93 | public PriceBuilder withBtc(BigDecimal ethToBtc) { 94 | this.ethbtc = ethToBtc; 95 | return this; 96 | } 97 | 98 | public PriceBuilder withTimestampUsd(LocalDateTime ethToUsdTimestamp) { 99 | this.ethusdTimestamp = ethToUsdTimestamp; 100 | return this; 101 | } 102 | 103 | public PriceBuilder withTimestampBtc(LocalDateTime ethToBtcTimestamp) { 104 | this.ethbtcTimestamp = ethToBtcTimestamp; 105 | return this; 106 | } 107 | 108 | public Price build() { 109 | Price price = new Price(); 110 | price.ethbtc = this.ethbtc; 111 | price.ethusd = this.ethusd; 112 | if (this.ethbtcTimestamp != null) { 113 | price.ethbtc_timestamp = String.valueOf(this.ethbtcTimestamp.toEpochSecond(ZoneOffset.UTC)); 114 | price._ethbtc_timestamp = this.ethbtcTimestamp; 115 | } 116 | if (this.ethusdTimestamp != null) { 117 | price.ethusd_timestamp = String.valueOf(this.ethusdTimestamp.toEpochSecond(ZoneOffset.UTC)); 118 | price._ethusd_timestamp = this.ethusdTimestamp; 119 | } 120 | return price; 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/Status.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model; 2 | 3 | import java.util.Objects; 4 | 5 | /** 6 | * Contract Execution Status 7 | * 8 | * @author GoodforGod 9 | * @since 30.10.2018 10 | */ 11 | public class Status { 12 | 13 | /** 14 | * "0" = Pass , isError":"1" = Error during Contract Execution 15 | */ 16 | private int isError; 17 | private String errDescription; 18 | 19 | protected Status() {} 20 | 21 | public boolean haveError() { 22 | return isError == 1; 23 | } 24 | 25 | public String getErrDescription() { 26 | return errDescription; 27 | } 28 | 29 | @Override 30 | public boolean equals(Object o) { 31 | if (this == o) 32 | return true; 33 | if (!(o instanceof Status)) 34 | return false; 35 | Status status = (Status) o; 36 | return isError == status.isError && Objects.equals(errDescription, status.errDescription); 37 | } 38 | 39 | @Override 40 | public int hashCode() { 41 | return Objects.hash(isError, errDescription); 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return "Status{" + 47 | "isError=" + isError + 48 | ", errDescription=" + errDescription + 49 | '}'; 50 | } 51 | 52 | public static StatusBuilder builder() { 53 | return new StatusBuilder(); 54 | } 55 | 56 | public static final class StatusBuilder { 57 | 58 | private int isError; 59 | private String errDescription; 60 | 61 | private StatusBuilder() {} 62 | 63 | public StatusBuilder withIsError(int isError) { 64 | this.isError = isError; 65 | return this; 66 | } 67 | 68 | public StatusBuilder withErrDescription(String errDescription) { 69 | this.errDescription = errDescription; 70 | return this; 71 | } 72 | 73 | public Status build() { 74 | Status status = new Status(); 75 | status.isError = this.isError; 76 | status.errDescription = this.errDescription; 77 | return status; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/TokenBalance.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model; 2 | 3 | import java.util.Objects; 4 | 5 | /** 6 | * @author GoodforGod 7 | * @since 31.10.2018 8 | */ 9 | public class TokenBalance extends Balance { 10 | 11 | private final String tokenContract; 12 | 13 | public TokenBalance(String address, Wei balance, String tokenContract) { 14 | super(address, balance); 15 | this.tokenContract = tokenContract; 16 | } 17 | 18 | public String getContract() { 19 | return tokenContract; 20 | } 21 | 22 | @Override 23 | public boolean equals(Object o) { 24 | if (this == o) 25 | return true; 26 | if (!(o instanceof TokenBalance)) 27 | return false; 28 | if (!super.equals(o)) 29 | return false; 30 | TokenBalance that = (TokenBalance) o; 31 | return Objects.equals(tokenContract, that.tokenContract); 32 | } 33 | 34 | @Override 35 | public int hashCode() { 36 | return Objects.hash(super.hashCode(), tokenContract); 37 | } 38 | 39 | @Override 40 | public String toString() { 41 | return "TokenBalance{" + 42 | "tokenContract=" + tokenContract + 43 | '}'; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/Wei.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model; 2 | 3 | import java.math.BigDecimal; 4 | import java.math.BigInteger; 5 | import java.math.RoundingMode; 6 | import java.util.Objects; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | /** 10 | * @author GoodforGod 11 | * @since 30.10.2018 12 | */ 13 | public class Wei implements Comparable { 14 | 15 | private static final BigDecimal KWEI_POW = BigDecimal.ONE.pow(3); 16 | private static final BigDecimal MWEI_POW = BigDecimal.ONE.pow(6); 17 | private static final BigDecimal GWEI_POW = BigDecimal.ONE.pow(9); 18 | private static final BigDecimal WEI_POW = BigDecimal.ONE.pow(18); 19 | 20 | private final BigInteger result; 21 | 22 | private Wei(BigInteger value) { 23 | this.result = value; 24 | } 25 | 26 | public static Wei ofWei(int value) { 27 | return ofWei(BigInteger.valueOf(value)); 28 | } 29 | 30 | public static Wei ofWei(long value) { 31 | return ofWei(BigInteger.valueOf(value)); 32 | } 33 | 34 | public static Wei ofWei(BigInteger value) { 35 | return new Wei(value); 36 | } 37 | 38 | public static Wei ofKwei(int value) { 39 | return ofKwei(BigInteger.valueOf(value)); 40 | } 41 | 42 | public static Wei ofKwei(long value) { 43 | return ofKwei(BigInteger.valueOf(value)); 44 | } 45 | 46 | public static Wei ofKwei(BigDecimal value) { 47 | return new Wei(value.multiply(KWEI_POW).toBigInteger()); 48 | } 49 | 50 | public static Wei ofKwei(BigInteger value) { 51 | return new Wei(value.multiply(KWEI_POW.toBigInteger())); 52 | } 53 | 54 | public static Wei ofMwei(int value) { 55 | return ofMwei(BigInteger.valueOf(value)); 56 | } 57 | 58 | public static Wei ofMwei(long value) { 59 | return ofMwei(BigInteger.valueOf(value)); 60 | } 61 | 62 | public static Wei ofMwei(BigDecimal value) { 63 | return new Wei(value.multiply(MWEI_POW).toBigInteger()); 64 | } 65 | 66 | public static Wei ofMwei(BigInteger value) { 67 | return new Wei(value.multiply(MWEI_POW.toBigInteger())); 68 | } 69 | 70 | public static Wei ofGwei(int value) { 71 | return ofGwei(BigInteger.valueOf(value)); 72 | } 73 | 74 | public static Wei ofGwei(long value) { 75 | return ofGwei(BigInteger.valueOf(value)); 76 | } 77 | 78 | public static Wei ofGwei(BigDecimal value) { 79 | return new Wei(value.multiply(GWEI_POW).toBigInteger()); 80 | } 81 | 82 | public static Wei ofGwei(BigInteger value) { 83 | return new Wei(value.multiply(GWEI_POW.toBigInteger())); 84 | } 85 | 86 | public static Wei ofEther(int value) { 87 | return ofEther(BigInteger.valueOf(value)); 88 | } 89 | 90 | public static Wei ofEther(long value) { 91 | return ofEther(BigInteger.valueOf(value)); 92 | } 93 | 94 | public static Wei ofEther(BigDecimal value) { 95 | return new Wei(value.multiply(WEI_POW).toBigInteger()); 96 | } 97 | 98 | public static Wei ofEther(BigInteger value) { 99 | return new Wei(value.multiply(WEI_POW.toBigInteger())); 100 | } 101 | 102 | public BigInteger asWei() { 103 | return result; 104 | } 105 | 106 | public BigDecimal asKwei() { 107 | return new BigDecimal(result).divide(KWEI_POW, RoundingMode.HALF_UP); 108 | } 109 | 110 | public BigDecimal asMwei() { 111 | return new BigDecimal(result).divide(MWEI_POW, RoundingMode.HALF_UP); 112 | } 113 | 114 | public BigDecimal asGwei() { 115 | return new BigDecimal(result).divide(GWEI_POW, RoundingMode.HALF_UP); 116 | } 117 | 118 | public BigDecimal asEther() { 119 | return new BigDecimal(result).divide(WEI_POW, RoundingMode.HALF_UP); 120 | } 121 | 122 | @Override 123 | public boolean equals(Object o) { 124 | if (this == o) 125 | return true; 126 | if (!(o instanceof Wei)) 127 | return false; 128 | Wei wei = (Wei) o; 129 | return Objects.equals(result, wei.result); 130 | } 131 | 132 | @Override 133 | public int hashCode() { 134 | return Objects.hash(result); 135 | } 136 | 137 | @Override 138 | public int compareTo(@NotNull Wei o) { 139 | return result.compareTo(o.result); 140 | } 141 | 142 | @Override 143 | public String toString() { 144 | return result.toString(); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BaseProxyTO.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.proxy.utility; 2 | 3 | /** 4 | * @author GoodforGod 5 | * @since 31.10.2018 6 | */ 7 | abstract class BaseProxyTO { 8 | 9 | private String id; 10 | private String jsonrpc; 11 | private ErrorProxyTO error; 12 | 13 | public String getId() { 14 | return id; 15 | } 16 | 17 | public String getJsonrpc() { 18 | return jsonrpc; 19 | } 20 | 21 | public ErrorProxyTO getError() { 22 | return error; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BlockProxyTO.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.proxy.utility; 2 | 3 | import io.goodforgod.api.etherscan.model.proxy.BlockProxy; 4 | 5 | /** 6 | * @author GoodforGod 7 | * @since 01.11.2018 8 | */ 9 | public class BlockProxyTO extends BaseProxyTO { 10 | 11 | private BlockProxy result; 12 | 13 | public BlockProxy getResult() { 14 | return result; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/ErrorProxyTO.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.proxy.utility; 2 | 3 | /** 4 | * @author GoodforGod 5 | * @since 03.11.2018 6 | */ 7 | public class ErrorProxyTO { 8 | 9 | private String message; 10 | private String code; 11 | 12 | public String getMessage() { 13 | return message; 14 | } 15 | 16 | public String getCode() { 17 | return code; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/StringProxyTO.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.proxy.utility; 2 | 3 | /** 4 | * @author GoodforGod 5 | * @since 31.10.2018 6 | */ 7 | public class StringProxyTO extends BaseProxyTO { 8 | 9 | private String result; 10 | 11 | public String getResult() { 12 | return result; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxInfoProxyTO.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.proxy.utility; 2 | 3 | import io.goodforgod.api.etherscan.model.proxy.ReceiptProxy; 4 | 5 | /** 6 | * @author GoodforGod 7 | * @since 03.11.2018 8 | */ 9 | public class TxInfoProxyTO extends BaseProxyTO { 10 | 11 | private ReceiptProxy result; 12 | 13 | public ReceiptProxy getResult() { 14 | return result; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxProxyTO.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.proxy.utility; 2 | 3 | import io.goodforgod.api.etherscan.model.proxy.TxProxy; 4 | 5 | /** 6 | * @author GoodforGod 7 | * @since 01.11.2018 8 | */ 9 | public class TxProxyTO extends BaseProxyTO { 10 | 11 | private TxProxy result; 12 | 13 | public TxProxy getResult() { 14 | return result; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/query/LogOp.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.query; 2 | 3 | /** 4 | * Part of The Event Log API 5 | * 6 | * @author GoodforGod 7 | * @since 31.10.2018 8 | */ 9 | public enum LogOp { 10 | 11 | AND("and"), 12 | OR("or"); 13 | 14 | private final String operation; 15 | 16 | LogOp(String operation) { 17 | this.operation = operation; 18 | } 19 | 20 | public String getOperation() { 21 | return operation; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/query/LogQuery.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.query; 2 | 3 | import static io.goodforgod.api.etherscan.model.query.LogQueryBuilderImpl.MAX_BLOCK; 4 | import static io.goodforgod.api.etherscan.model.query.LogQueryBuilderImpl.MIN_BLOCK; 5 | 6 | import io.goodforgod.api.etherscan.LogsAPI; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | /** 10 | * Final built container for The Event Log API 11 | * EtherScan - API Descriptions ... 12 | * 13 | * @see LogQueryBuilderImpl 14 | * @see LogsAPI 15 | * @author GoodforGod 16 | * @since 10.05.2023 17 | */ 18 | public interface LogQuery { 19 | 20 | @NotNull 21 | String params(); 22 | 23 | @NotNull 24 | static Builder builder(@NotNull String address) { 25 | return new LogQueryBuilderImpl(address, MIN_BLOCK, MAX_BLOCK); 26 | } 27 | 28 | interface Builder { 29 | 30 | @NotNull 31 | LogQuery.Builder withBlockFrom(long startBlock); 32 | 33 | @NotNull 34 | LogQuery.Builder withBlockTo(long endBlock); 35 | 36 | @NotNull 37 | LogTopicSingle withTopic(@NotNull String topic0); 38 | 39 | @NotNull 40 | LogTopicTuple withTopic(@NotNull String topic0, @NotNull String topic1); 41 | 42 | @NotNull 43 | LogTopicTriple withTopic(@NotNull String topic0, @NotNull String topic1, @NotNull String topic2); 44 | 45 | @NotNull 46 | LogTopicQuadro withTopic(@NotNull String topic0, @NotNull String topic1, @NotNull String topic2, @NotNull String topic3); 47 | 48 | @NotNull 49 | LogQuery build(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryBuilderImpl.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.query; 2 | 3 | import io.goodforgod.api.etherscan.LogsAPI; 4 | import io.goodforgod.api.etherscan.error.EtherScanLogQueryException; 5 | import io.goodforgod.api.etherscan.util.BasicUtils; 6 | import org.jetbrains.annotations.NotNull; 7 | 8 | /** 9 | * Builder for The Event Log API 10 | * 11 | * @see LogsAPI 12 | * @author GoodforGod 13 | * @since 31.10.2018 14 | */ 15 | final class LogQueryBuilderImpl implements LogQuery.Builder { 16 | 17 | static final long MIN_BLOCK = 0; 18 | static final long MAX_BLOCK = 99999999999999999L; 19 | 20 | private final String address; 21 | private final long startBlock, endBlock; 22 | 23 | LogQueryBuilderImpl(String address, long startBlock, long endBlock) { 24 | BasicUtils.validateAddress(address); 25 | this.address = address; 26 | this.startBlock = startBlock; 27 | this.endBlock = endBlock; 28 | } 29 | 30 | @Override 31 | public @NotNull LogQuery.Builder withBlockFrom(long startBlock) { 32 | return new LogQueryBuilderImpl(this.address, startBlock, this.endBlock); 33 | } 34 | 35 | @Override 36 | public @NotNull LogQuery.Builder withBlockTo(long endBlock) { 37 | return new LogQueryBuilderImpl(this.address, this.startBlock, endBlock); 38 | } 39 | 40 | @Override 41 | public @NotNull LogTopicSingle withTopic(@NotNull String topic0) { 42 | if (BasicUtils.isNotHex(topic0)) 43 | throw new EtherScanLogQueryException("topic0 can not be empty or non hex."); 44 | return new LogTopicSingle(address, startBlock, endBlock, topic0); 45 | } 46 | 47 | @Override 48 | public @NotNull LogTopicTuple withTopic(@NotNull String topic0, @NotNull String topic1) { 49 | if (BasicUtils.isNotHex(topic0)) 50 | throw new EtherScanLogQueryException("topic0 can not be empty or non hex."); 51 | if (BasicUtils.isNotHex(topic1)) 52 | throw new EtherScanLogQueryException("topic1 can not be empty or non hex."); 53 | return new LogTopicTuple(address, startBlock, endBlock, topic0, topic1); 54 | } 55 | 56 | @Override 57 | public @NotNull LogTopicTriple withTopic(@NotNull String topic0, @NotNull String topic1, @NotNull String topic2) { 58 | if (BasicUtils.isNotHex(topic0)) 59 | throw new EtherScanLogQueryException("topic0 can not be empty or non hex."); 60 | if (BasicUtils.isNotHex(topic1)) 61 | throw new EtherScanLogQueryException("topic1 can not be empty or non hex."); 62 | if (BasicUtils.isNotHex(topic2)) 63 | throw new EtherScanLogQueryException("topic2 can not be empty or non hex."); 64 | return new LogTopicTriple(address, startBlock, endBlock, topic0, topic1, topic2); 65 | } 66 | 67 | @Override 68 | public @NotNull LogTopicQuadro 69 | withTopic(@NotNull String topic0, @NotNull String topic1, @NotNull String topic2, @NotNull String topic3) { 70 | if (BasicUtils.isNotHex(topic0)) 71 | throw new EtherScanLogQueryException("topic0 can not be empty or non hex."); 72 | if (BasicUtils.isNotHex(topic1)) 73 | throw new EtherScanLogQueryException("topic1 can not be empty or non hex."); 74 | if (BasicUtils.isNotHex(topic2)) 75 | throw new EtherScanLogQueryException("topic2 can not be empty or non hex."); 76 | if (BasicUtils.isNotHex(topic3)) 77 | throw new EtherScanLogQueryException("topic3 can not be empty or non hex."); 78 | 79 | return new LogTopicQuadro(address, startBlock, endBlock, topic0, topic1, topic2, topic3); 80 | } 81 | 82 | @Override 83 | public @NotNull LogQuery build() throws EtherScanLogQueryException { 84 | return new LogQueryImpl("&address=" + this.address + "&fromBlock=" + this.startBlock + "&toBlock=" + this.endBlock); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryImpl.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.query; 2 | 3 | import io.goodforgod.api.etherscan.LogsAPI; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | /** 7 | * Final builded container for The Event Log API 8 | * EtherScan - API Descriptions ... 9 | * 10 | * @see LogQueryBuilderImpl 11 | * @see LogsAPI 12 | * @author GoodforGod 13 | * @since 31.10.2018 14 | */ 15 | final class LogQueryImpl implements LogQuery { 16 | 17 | /** 18 | * Final request parameter for api call 19 | */ 20 | private final String params; 21 | 22 | LogQueryImpl(String params) { 23 | this.params = params; 24 | } 25 | 26 | @Override 27 | public @NotNull String params() { 28 | return params; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryParams.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.query; 2 | 3 | import io.goodforgod.api.etherscan.LogsAPI; 4 | 5 | /** 6 | * Base parameters for The Event Log API builder 7 | * 8 | * @see LogQueryBuilderImpl 9 | * @see LogsAPI 10 | * @author GoodforGod 11 | * @since 31.10.2018 12 | */ 13 | final class LogQueryParams { 14 | 15 | private LogQueryParams() {} 16 | 17 | static final String FROM_BLOCK_PARAM = "&fromBlock="; 18 | static final String TO_BLOCK_PARAM = "&toBlock="; 19 | static final String ADDRESS_PARAM = "&address="; 20 | 21 | static final String TOPIC_0_PARAM = "&topic0=", 22 | TOPIC_1_PARAM = "&topic1=", 23 | TOPIC_2_PARAM = "&topic2=", 24 | TOPIC_3_PARAM = "&topic3="; 25 | 26 | static final String TOPIC_0_1_OPR_PARAM = "&topic0_1_opr=", 27 | TOPIC_1_2_OPR_PARAM = "&topic1_2_opr=", 28 | TOPIC_2_3_OPR_PARAM = "&topic2_3_opr=", 29 | TOPIC_0_2_OPR_PARAM = "&topic0_2_opr=", 30 | TOPIC_0_3_OPR_PARAM = "&topic0_3_opr=", 31 | TOPIC_1_3_OPR_PARAM = "&topic1_3_opr="; 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicBuilder.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.query; 2 | 3 | import org.jetbrains.annotations.NotNull; 4 | 5 | /** 6 | * @see LogTopicSingle 7 | * @see LogTopicTuple 8 | * @see LogTopicTriple 9 | * @see LogTopicQuadro 10 | * @author GoodforGod 11 | * @since 10.05.2023 12 | */ 13 | public interface LogTopicBuilder { 14 | 15 | @NotNull 16 | LogQuery build(); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicQuadro.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.query; 2 | 3 | import static io.goodforgod.api.etherscan.model.query.LogQueryParams.*; 4 | 5 | import io.goodforgod.api.etherscan.LogsAPI; 6 | import io.goodforgod.api.etherscan.error.EtherScanLogQueryException; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | /** 10 | * Quadro topic parameter builder for The Event Log API 11 | * 12 | * @see LogQueryBuilderImpl 13 | * @see LogsAPI 14 | * @author GoodforGod 15 | * @since 31.10.2018 16 | */ 17 | public final class LogTopicQuadro implements LogTopicBuilder { 18 | 19 | private final String address; 20 | private final long startBlock, endBlock; 21 | private final String topic0, topic1, topic2, topic3; 22 | 23 | private LogOp topic0_1_opr, topic1_2_opr, topic2_3_opr, topic0_2_opr, topic0_3_opr, topic1_3_opr; 24 | 25 | LogTopicQuadro(String address, 26 | long startBlock, 27 | long endBlock, 28 | String topic0, 29 | String topic1, 30 | String topic2, 31 | String topic3) { 32 | this.address = address; 33 | this.startBlock = startBlock; 34 | this.endBlock = endBlock; 35 | this.topic0 = topic0; 36 | this.topic1 = topic1; 37 | this.topic2 = topic2; 38 | this.topic3 = topic3; 39 | } 40 | 41 | public LogTopicQuadro setOpTopic0_1(LogOp topic0_1_opr) { 42 | this.topic0_1_opr = topic0_1_opr; 43 | return this; 44 | } 45 | 46 | public LogTopicQuadro setOpTopic1_2(LogOp topic1_2_opr) { 47 | this.topic1_2_opr = topic1_2_opr; 48 | return this; 49 | } 50 | 51 | public LogTopicQuadro setOpTopic2_3(LogOp topic2_3_opr) { 52 | this.topic2_3_opr = topic2_3_opr; 53 | return this; 54 | } 55 | 56 | public LogTopicQuadro setOpTopic0_2(LogOp topic0_2_opr) { 57 | this.topic0_2_opr = topic0_2_opr; 58 | return this; 59 | } 60 | 61 | public LogTopicQuadro setOpTopic0_3(LogOp topic0_3_opr) { 62 | this.topic0_3_opr = topic0_3_opr; 63 | return this; 64 | } 65 | 66 | public LogTopicQuadro setOpTopic1_3(LogOp topic1_3_opr) { 67 | this.topic1_3_opr = topic1_3_opr; 68 | return this; 69 | } 70 | 71 | @Override 72 | public @NotNull LogQuery build() { 73 | if (topic0_1_opr == null) 74 | throw new EtherScanLogQueryException("topic0_1_opr can not be null."); 75 | if (topic0_2_opr == null) 76 | throw new EtherScanLogQueryException("topic0_2_opr can not be null."); 77 | if (topic0_3_opr == null) 78 | throw new EtherScanLogQueryException("topic0_3_opr can not be null."); 79 | if (topic1_2_opr == null) 80 | throw new EtherScanLogQueryException("topic1_2_opr can not be null."); 81 | if (topic2_3_opr == null) 82 | throw new EtherScanLogQueryException("topic2_3_opr can not be null."); 83 | if (topic1_3_opr == null) 84 | throw new EtherScanLogQueryException("topic1_3_opr can not be null."); 85 | 86 | return new LogQueryImpl(ADDRESS_PARAM + address 87 | + FROM_BLOCK_PARAM + startBlock + TO_BLOCK_PARAM + endBlock 88 | + TOPIC_0_PARAM + topic0 89 | + TOPIC_1_PARAM + topic1 90 | + TOPIC_2_PARAM + topic2 91 | + TOPIC_3_PARAM + topic3 92 | + TOPIC_0_1_OPR_PARAM + topic0_1_opr.getOperation() 93 | + TOPIC_0_2_OPR_PARAM + topic0_2_opr.getOperation() 94 | + TOPIC_0_3_OPR_PARAM + topic0_2_opr.getOperation() 95 | + TOPIC_1_2_OPR_PARAM + topic0_2_opr.getOperation() 96 | + TOPIC_1_3_OPR_PARAM + topic1_2_opr.getOperation() 97 | + TOPIC_2_3_OPR_PARAM + topic0_2_opr.getOperation()); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicSingle.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.query; 2 | 3 | import static io.goodforgod.api.etherscan.model.query.LogQueryParams.*; 4 | 5 | import io.goodforgod.api.etherscan.LogsAPI; 6 | import io.goodforgod.api.etherscan.error.EtherScanLogQueryException; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | /** 10 | * Single topic parameter builder for The Event Log API 11 | * 12 | * @see LogQueryBuilderImpl 13 | * @see LogsAPI 14 | * @author GoodforGod 15 | * @since 31.10.2018 16 | */ 17 | public final class LogTopicSingle implements LogTopicBuilder { 18 | 19 | private final String address; 20 | private final long startBlock, endBlock; 21 | 22 | private final String topic0; 23 | 24 | LogTopicSingle(String address, long startBlock, long endBlock, String topic0) { 25 | this.address = address; 26 | this.startBlock = startBlock; 27 | this.endBlock = endBlock; 28 | this.topic0 = topic0; 29 | } 30 | 31 | @Override 32 | public @NotNull LogQuery build() throws EtherScanLogQueryException { 33 | return new LogQueryImpl(ADDRESS_PARAM + address 34 | + FROM_BLOCK_PARAM + startBlock + TO_BLOCK_PARAM + endBlock 35 | + TOPIC_0_PARAM + topic0); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTriple.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.query; 2 | 3 | import static io.goodforgod.api.etherscan.model.query.LogQueryParams.*; 4 | 5 | import io.goodforgod.api.etherscan.LogsAPI; 6 | import io.goodforgod.api.etherscan.error.EtherScanLogQueryException; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | /** 10 | * Triple topic parameter builder for The Event Log API 11 | * 12 | * @see LogQueryBuilderImpl 13 | * @see LogsAPI 14 | * @author GoodforGod 15 | * @since 31.10.2018 16 | */ 17 | public final class LogTopicTriple implements LogTopicBuilder { 18 | 19 | private final String address; 20 | private final long startBlock, endBlock; 21 | private final String topic0, topic1, topic2; 22 | 23 | private LogOp topic0_1_opr, topic1_2_opr, topic0_2_opr; 24 | 25 | LogTopicTriple(String address, 26 | long startBlock, 27 | long endBlock, 28 | String topic0, 29 | String topic1, 30 | String topic2) { 31 | this.address = address; 32 | this.startBlock = startBlock; 33 | this.endBlock = endBlock; 34 | this.topic0 = topic0; 35 | this.topic1 = topic1; 36 | this.topic2 = topic2; 37 | } 38 | 39 | public LogTopicTriple setOpTopic0_1(LogOp topic0_1_opr) { 40 | this.topic0_1_opr = topic0_1_opr; 41 | return this; 42 | } 43 | 44 | public LogTopicTriple setOpTopic0_2(LogOp topic0_2_opr) { 45 | this.topic0_2_opr = topic0_2_opr; 46 | return this; 47 | } 48 | 49 | public LogTopicTriple setOpTopic1_2(LogOp topic1_2_opr) { 50 | this.topic1_2_opr = topic1_2_opr; 51 | return this; 52 | } 53 | 54 | @Override 55 | public @NotNull LogQuery build() throws EtherScanLogQueryException { 56 | if (topic0_1_opr == null) 57 | throw new EtherScanLogQueryException("topic0_1_opr can not be null."); 58 | if (topic0_2_opr == null) 59 | throw new EtherScanLogQueryException("topic0_2_opr can not be null."); 60 | if (topic1_2_opr == null) 61 | throw new EtherScanLogQueryException("topic1_2_opr can not be null."); 62 | 63 | return new LogQueryImpl(ADDRESS_PARAM + address 64 | + FROM_BLOCK_PARAM + startBlock + TO_BLOCK_PARAM + endBlock 65 | + TOPIC_0_PARAM + topic0 66 | + TOPIC_1_PARAM + topic1 67 | + TOPIC_2_PARAM + topic2 68 | + TOPIC_0_1_OPR_PARAM + topic0_1_opr.getOperation() 69 | + TOPIC_1_2_OPR_PARAM + topic1_2_opr.getOperation() 70 | + TOPIC_0_2_OPR_PARAM + topic0_2_opr.getOperation()); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTuple.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.query; 2 | 3 | import static io.goodforgod.api.etherscan.model.query.LogQueryParams.*; 4 | 5 | import io.goodforgod.api.etherscan.LogsAPI; 6 | import io.goodforgod.api.etherscan.error.EtherScanLogQueryException; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | /** 10 | * Tuple topic parameter builder for The Event Log API 11 | * 12 | * @see LogQueryBuilderImpl 13 | * @see LogsAPI 14 | * @author GoodforGod 15 | * @since 31.10.2018 16 | */ 17 | public final class LogTopicTuple implements LogTopicBuilder { 18 | 19 | private final String address; 20 | private final long startBlock, endBlock; 21 | private final String topic0, topic1; 22 | 23 | private LogOp topic0_1_opr; 24 | 25 | LogTopicTuple(String address, 26 | long startBlock, 27 | long endBlock, 28 | String topic0, 29 | String topic1) { 30 | this.address = address; 31 | this.startBlock = startBlock; 32 | this.endBlock = endBlock; 33 | this.topic0 = topic0; 34 | this.topic1 = topic1; 35 | } 36 | 37 | public LogTopicTuple setOpTopic0_1(LogOp topic0_1_opr) { 38 | this.topic0_1_opr = topic0_1_opr; 39 | return this; 40 | } 41 | 42 | @Override 43 | public @NotNull LogQuery build() throws EtherScanLogQueryException { 44 | if (topic0_1_opr == null) 45 | throw new EtherScanLogQueryException("topic0_1_opr can not be null."); 46 | 47 | return new LogQueryImpl(ADDRESS_PARAM + address 48 | + FROM_BLOCK_PARAM + startBlock + TO_BLOCK_PARAM + endBlock 49 | + TOPIC_0_PARAM + topic0 50 | + TOPIC_1_PARAM + topic1 51 | + TOPIC_0_1_OPR_PARAM + topic0_1_opr.getOperation()); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/response/BalanceResponseTO.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.response; 2 | 3 | /** 4 | * @author GoodforGod 5 | * @since 29.10.2018 6 | */ 7 | public class BalanceResponseTO extends BaseListResponseTO { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/response/BalanceTO.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.response; 2 | 3 | /** 4 | * @author GoodforGod 5 | * @since 29.10.2018 6 | */ 7 | public class BalanceTO { 8 | 9 | private String account; 10 | private String balance; 11 | 12 | public String getAccount() { 13 | return account; 14 | } 15 | 16 | public String getBalance() { 17 | return balance; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/response/BaseListResponseTO.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.response; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * @author GoodforGod 7 | * @since 30.10.2018 8 | */ 9 | public abstract class BaseListResponseTO extends BaseResponseTO { 10 | 11 | private List result; 12 | 13 | public List getResult() { 14 | return result; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/response/BaseResponseTO.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.response; 2 | 3 | import io.goodforgod.api.etherscan.util.BasicUtils; 4 | 5 | /** 6 | * @author GoodforGod 7 | * @since 29.10.2018 8 | */ 9 | public abstract class BaseResponseTO { 10 | 11 | String status; 12 | String message; 13 | 14 | public int getStatus() { 15 | return BasicUtils.isEmpty(status) 16 | ? -1 17 | : Integer.parseInt(status); 18 | } 19 | 20 | public String getMessage() { 21 | return message; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/response/BlockParam.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.response; 2 | 3 | /** 4 | * @author GoodforGod 5 | * @since 31.10.2018 6 | */ 7 | public class BlockParam { 8 | 9 | private final long startBlock; 10 | private final long endBlock; 11 | 12 | public BlockParam(long startBlock, long endBlock) { 13 | this.startBlock = startBlock; 14 | this.endBlock = endBlock; 15 | } 16 | 17 | public long start() { 18 | return startBlock; 19 | } 20 | 21 | public long end() { 22 | return endBlock; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/response/BlockResponseTO.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.response; 2 | 3 | import io.goodforgod.api.etherscan.model.Block; 4 | 5 | /** 6 | * @author GoodforGod 7 | * @since 30.10.2018 8 | */ 9 | public class BlockResponseTO extends BaseListResponseTO { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/response/ContractCreationResponseTO.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.response; 2 | 3 | public class ContractCreationResponseTO extends BaseListResponseTO {} 4 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/response/ContractCreationTO.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.response; 2 | 3 | public class ContractCreationTO { 4 | 5 | private String contractAddress; 6 | private String contractCreator; 7 | private String txHash; 8 | 9 | public String getContractAddress() { 10 | return contractAddress; 11 | } 12 | 13 | public String getContractCreator() { 14 | return contractCreator; 15 | } 16 | 17 | public String getTxHash() { 18 | return txHash; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/response/EthSupplyResponseTO.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.response; 2 | 3 | import io.goodforgod.api.etherscan.model.EthSupply; 4 | 5 | /** 6 | * @author GoodforGod 7 | * @since 14.05.2023 8 | */ 9 | public class EthSupplyResponseTO extends BaseResponseTO { 10 | 11 | private EthSupply result; 12 | 13 | public EthSupply getResult() { 14 | return result; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/response/GasEstimateResponseTO.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.response; 2 | 3 | /** 4 | * @author Abhay Gupta 5 | * @since 14.11.2022 6 | */ 7 | public class GasEstimateResponseTO extends BaseResponseTO { 8 | 9 | private String result; 10 | 11 | public String getResult() { 12 | return result; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/response/GasOracleResponseTO.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.response; 2 | 3 | import io.goodforgod.api.etherscan.model.GasOracle; 4 | 5 | /** 6 | * @author Abhay Gupta 7 | * @since 14.11.2022 8 | */ 9 | public class GasOracleResponseTO extends BaseResponseTO { 10 | 11 | private GasOracle result; 12 | 13 | public GasOracle getResult() { 14 | return result; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/response/LogResponseTO.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.response; 2 | 3 | import io.goodforgod.api.etherscan.model.Log; 4 | 5 | /** 6 | * @author GoodforGod 7 | * @since 31.10.2018 8 | */ 9 | public class LogResponseTO extends BaseListResponseTO { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/response/PriceResponseTO.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.response; 2 | 3 | import io.goodforgod.api.etherscan.model.Price; 4 | 5 | /** 6 | * @author GoodforGod 7 | * @since 30.10.2018 8 | */ 9 | public class PriceResponseTO extends BaseResponseTO { 10 | 11 | private Price result; 12 | 13 | public Price getResult() { 14 | return result; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/response/ReceiptStatusResponseTO.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.response; 2 | 3 | /** 4 | * @author GoodforGod 5 | * @since 03.11.2018 6 | */ 7 | public class ReceiptStatusResponseTO extends BaseResponseTO { 8 | 9 | private ReceiptStatusTO result; 10 | 11 | public ReceiptStatusTO getResult() { 12 | return result; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/response/ReceiptStatusTO.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.response; 2 | 3 | /** 4 | * @author GoodforGod 5 | * @since 03.11.2018 6 | */ 7 | public class ReceiptStatusTO { 8 | 9 | private String status; 10 | 11 | public String getStatus() { 12 | return status; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/response/StatusResponseTO.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.response; 2 | 3 | import io.goodforgod.api.etherscan.model.Status; 4 | 5 | /** 6 | * @author GoodforGod 7 | * @since 30.10.2018 8 | */ 9 | public class StatusResponseTO extends BaseResponseTO { 10 | 11 | private Status result; 12 | 13 | public Status getResult() { 14 | return result; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/response/StringResponseTO.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.response; 2 | 3 | /** 4 | * @author GoodforGod 5 | * @since 29.10.2018 6 | */ 7 | public class StringResponseTO extends BaseResponseTO { 8 | 9 | private String result; 10 | 11 | public String getResult() { 12 | return result; 13 | } 14 | 15 | public static StringResponseBuilder builder() { 16 | return new StringResponseBuilder(); 17 | } 18 | 19 | public static final class StringResponseBuilder { 20 | 21 | private String status; 22 | private String message; 23 | private String result; 24 | 25 | private StringResponseBuilder() {} 26 | 27 | public StringResponseBuilder withStatus(String status) { 28 | this.status = status; 29 | return this; 30 | } 31 | 32 | public StringResponseBuilder withMessage(String message) { 33 | this.message = message; 34 | return this; 35 | } 36 | 37 | public StringResponseBuilder withResult(String result) { 38 | this.result = result; 39 | return this; 40 | } 41 | 42 | public StringResponseTO build() { 43 | StringResponseTO stringResponseTO = new StringResponseTO(); 44 | stringResponseTO.status = this.status; 45 | stringResponseTO.message = this.message; 46 | stringResponseTO.result = this.result; 47 | return stringResponseTO; 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/response/TxErc1155ResponseTO.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.response; 2 | 3 | import io.goodforgod.api.etherscan.model.TxErc1155; 4 | 5 | /** 6 | * @author GoodforGod 7 | * @since 14.05.2023 8 | */ 9 | public class TxErc1155ResponseTO extends BaseListResponseTO { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/response/TxErc20ResponseTO.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.response; 2 | 3 | import io.goodforgod.api.etherscan.model.TxErc20; 4 | 5 | /** 6 | * @author GoodforGod 7 | * @since 29.10.2018 8 | */ 9 | public class TxErc20ResponseTO extends BaseListResponseTO { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/response/TxErc721ResponseTO.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.response; 2 | 3 | import io.goodforgod.api.etherscan.model.TxErc721; 4 | 5 | /** 6 | * @author GoodforGod 7 | * @since 14.05.2023 8 | */ 9 | public class TxErc721ResponseTO extends BaseListResponseTO { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/response/TxInternalResponseTO.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.response; 2 | 3 | import io.goodforgod.api.etherscan.model.TxInternal; 4 | 5 | /** 6 | * @author GoodforGod 7 | * @since 29.10.2018 8 | */ 9 | public class TxInternalResponseTO extends BaseListResponseTO { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/response/TxResponseTO.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.response; 2 | 3 | import io.goodforgod.api.etherscan.model.Tx; 4 | 5 | /** 6 | * @author GoodforGod 7 | * @since 29.10.2018 8 | */ 9 | public class TxResponseTO extends BaseListResponseTO { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/io/goodforgod/api/etherscan/model/response/UncleBlockResponseTO.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.model.response; 2 | 3 | import io.goodforgod.api.etherscan.model.BlockUncle; 4 | 5 | /** 6 | * @author GoodforGod 7 | * @since 30.10.2018 8 | */ 9 | public class UncleBlockResponseTO extends BaseResponseTO { 10 | 11 | private BlockUncle result; 12 | 13 | public BlockUncle getResult() { 14 | return result; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/ApiRunner.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan; 2 | 3 | import io.goodforgod.api.etherscan.manager.RequestQueueManager; 4 | import io.goodforgod.api.etherscan.util.BasicUtils; 5 | import java.util.Map; 6 | import org.junit.jupiter.api.AfterAll; 7 | import org.junit.jupiter.api.Assertions; 8 | 9 | public class ApiRunner extends Assertions { 10 | 11 | private static final String DEFAULT_KEY = "YourApiKeyToken"; 12 | 13 | private static final String API_KEY; 14 | private static final EtherScanAPI API; 15 | 16 | static { 17 | API_KEY = System.getenv().entrySet().stream() 18 | .filter(e -> e.getKey().startsWith("ETHERSCAN_API_KEY")) 19 | .filter(e -> !BasicUtils.isBlank(e.getValue())) 20 | .map(Map.Entry::getValue) 21 | .findFirst() 22 | .orElse(DEFAULT_KEY); 23 | 24 | final RequestQueueManager queueManager = (DEFAULT_KEY.equals(API_KEY)) 25 | ? RequestQueueManager.anonymous() 26 | : RequestQueueManager.planFree(); 27 | 28 | API = EtherScanAPI.builder() 29 | .withApiKey(ApiRunner.API_KEY) 30 | .withNetwork(EthNetworks.MAINNET) 31 | .withQueue(queueManager) 32 | .withRetryOnRateLimit(5) 33 | .build(); 34 | } 35 | 36 | public static EtherScanAPI getApi() { 37 | return API; 38 | } 39 | 40 | @AfterAll 41 | public static void cleanup() throws Exception { 42 | API.close(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/EtherScanAPITests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan; 2 | 3 | import io.goodforgod.api.etherscan.error.EtherScanConnectionException; 4 | import io.goodforgod.api.etherscan.error.EtherScanKeyException; 5 | import io.goodforgod.api.etherscan.http.EthHttpClient; 6 | import io.goodforgod.api.etherscan.http.impl.UrlEthHttpClient; 7 | import io.goodforgod.api.etherscan.model.Balance; 8 | import java.net.URI; 9 | import java.time.Duration; 10 | import java.util.concurrent.TimeUnit; 11 | import java.util.function.Supplier; 12 | import org.junit.jupiter.api.Test; 13 | 14 | /** 15 | * @author GoodforGod 16 | * @since 05.11.2018 17 | */ 18 | class EtherScanAPITests extends ApiRunner { 19 | 20 | private final EthNetworks network = EthNetworks.SEPOLIA; 21 | 22 | @Test 23 | void validKey() { 24 | String validKey = "YourKey"; 25 | EtherScanAPI api = EtherScanAPI.builder().withApiKey(validKey).withNetwork(network).build(); 26 | assertNotNull(api); 27 | } 28 | 29 | @Test 30 | void emptyKey() { 31 | assertThrows(EtherScanKeyException.class, () -> EtherScanAPI.builder().withApiKey("").build()); 32 | } 33 | 34 | @Test 35 | void blankKey() { 36 | assertThrows(EtherScanKeyException.class, 37 | () -> EtherScanAPI.builder().withApiKey(" ").withNetwork(network).build()); 38 | } 39 | 40 | @Test 41 | void noTimeoutOnRead() { 42 | Supplier supplier = () -> new UrlEthHttpClient(Duration.ofMillis(300)); 43 | EtherScanAPI api = EtherScanAPI.builder().withNetwork(EthNetworks.MAINNET).withHttpClient(supplier).build(); 44 | Balance balance = api.account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); 45 | assertNotNull(balance); 46 | } 47 | 48 | @Test 49 | void noTimeoutOnReadGroli() { 50 | Balance balance = getApi().account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); 51 | assertNotNull(balance); 52 | } 53 | 54 | @Test 55 | void noTimeoutOnReadTobalala() { 56 | Balance balance = getApi().account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); 57 | assertNotNull(balance); 58 | } 59 | 60 | @Test 61 | void noTimeoutUnlimitedAwait() { 62 | Balance balance = getApi().account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); 63 | assertNotNull(balance); 64 | } 65 | 66 | @Test 67 | void timeout() throws InterruptedException { 68 | TimeUnit.SECONDS.sleep(5); 69 | Supplier supplier = () -> new UrlEthHttpClient(Duration.ofMillis(300), Duration.ofMillis(300)); 70 | EtherScanAPI api = EtherScanAPI.builder() 71 | .withNetwork(() -> URI.create("https://api-unknown.etherscan.io/api")) 72 | .withHttpClient(supplier) 73 | .build(); 74 | 75 | assertThrows(EtherScanConnectionException.class, 76 | () -> api.account().blocksMined("0x0010f94b296A852aAac52EA6c5Ac72e03afD032D")); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceListTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.account; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; 5 | import io.goodforgod.api.etherscan.model.Balance; 6 | import io.goodforgod.api.etherscan.support.AddressUtil; 7 | import java.math.BigInteger; 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import org.junit.jupiter.api.Test; 11 | 12 | /** 13 | * @author GoodforGod 14 | * @since 03.11.2018 15 | */ 16 | class AccountBalanceListTests extends ApiRunner { 17 | 18 | @Test 19 | void correct() { 20 | List addresses = new ArrayList<>(); 21 | addresses.add("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9"); 22 | addresses.add("0xC9F32CE1127e44C51cbD182D6364F3D707Fd0d47"); 23 | 24 | List balances = getApi().account().balances(addresses); 25 | assertNotNull(balances); 26 | assertFalse(balances.isEmpty()); 27 | assertEquals(2, balances.size()); 28 | assertNotEquals(balances.get(0), balances.get(1)); 29 | assertNotEquals(balances.get(0).hashCode(), balances.get(1).hashCode()); 30 | for (Balance balance : balances) { 31 | assertNotNull(balance.getAddress()); 32 | assertNotNull(balance.getBalanceInWei()); 33 | assertNotNull(balance.getAddress()); 34 | assertNotEquals(BigInteger.ZERO, balance.getBalanceInWei().asWei()); 35 | assertNotNull(balance.toString()); 36 | } 37 | } 38 | 39 | @Test 40 | void correctMoreThat20Addresses() { 41 | List addresses = AddressUtil.genRealAddresses(); 42 | 43 | List balances = getApi().account().balances(addresses); 44 | assertNotNull(balances); 45 | assertFalse(balances.isEmpty()); 46 | assertEquals(25, balances.size()); 47 | for (Balance balance : balances) { 48 | assertNotNull(balance.getAddress()); 49 | } 50 | 51 | assertNotEquals(balances.get(0), balances.get(1)); 52 | } 53 | 54 | @Test 55 | void invalidParamWithError() { 56 | List addresses = new ArrayList<>(); 57 | addresses.add("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9"); 58 | addresses.add("C9F32CE1127e44C51cbD182D6364F3D707Fd0d47"); 59 | 60 | assertThrows(EtherScanInvalidAddressException.class, () -> getApi().account().balances(addresses)); 61 | } 62 | 63 | @Test 64 | void emptyParamList() { 65 | List addresses = new ArrayList<>(); 66 | List balances = getApi().account().balances(addresses); 67 | assertNotNull(balances); 68 | assertTrue(balances.isEmpty()); 69 | } 70 | 71 | @Test 72 | void correctParamWithEmptyExpectedResult() { 73 | List addresses = new ArrayList<>(); 74 | addresses.add("0x1327cb34984c3992ec1EA0eAE98Ccf80A74f95B9"); 75 | addresses.add("0xC1F32CE1127e44C51cbD182D6364F3D707Fd0d47"); 76 | 77 | List balances = getApi().account().balances(addresses); 78 | assertNotNull(balances); 79 | assertFalse(balances.isEmpty()); 80 | assertEquals(2, balances.size()); 81 | for (Balance balance : balances) { 82 | assertNotNull(balance.getAddress()); 83 | assertEquals(0, balance.getBalanceInWei().asWei().intValue()); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.account; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.EtherScanAPI; 5 | import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; 6 | import io.goodforgod.api.etherscan.model.Balance; 7 | import org.junit.jupiter.api.Test; 8 | 9 | /** 10 | * @author GoodforGod 11 | * @since 03.11.2018 12 | */ 13 | class AccountBalanceTests extends ApiRunner { 14 | 15 | private final EtherScanAPI api = getApi(); 16 | 17 | @Test 18 | void correct() { 19 | Balance balance = api.account().balance("0x8d4426f94e42f721C7116E81d6688cd935cB3b4F"); 20 | assertNotNull(balance); 21 | assertNotNull(balance.getBalanceInWei()); 22 | assertNotNull(balance.getAddress()); 23 | assertNotNull(balance.toString()); 24 | } 25 | 26 | @Test 27 | void invalidParamWithError() { 28 | assertThrows(EtherScanInvalidAddressException.class, 29 | () -> getApi().account().balance("8d4426f94e42f721C7116E81d6688cd935cB3b4F")); 30 | } 31 | 32 | @Test 33 | void correctParamWithEmptyExpectedResult() { 34 | Balance balance = api.account().balance("0x1d4426f94e42f721C7116E81d6688cd935cB3b4F"); 35 | assertNotNull(balance); 36 | assertNotNull(balance.getBalanceInWei()); 37 | assertNotNull(balance.getAddress()); 38 | assertEquals(0, balance.getBalanceInWei().asWei().intValue()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/account/AccountMinedBlocksTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.account; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.EtherScanAPI; 5 | import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; 6 | import io.goodforgod.api.etherscan.model.Block; 7 | import java.util.List; 8 | import org.junit.jupiter.api.Test; 9 | 10 | /** 11 | * @author GoodforGod 12 | * @since 03.11.2018 13 | */ 14 | class AccountMinedBlocksTests extends ApiRunner { 15 | 16 | private final EtherScanAPI api = getApi(); 17 | 18 | @Test 19 | void correct() { 20 | List blocks = api.account().blocksMined("0xE4C6175183029A0f039bf2DFffa5C6e8F3cA9B23"); 21 | assertNotNull(blocks); 22 | 23 | assertEquals(223, blocks.size()); 24 | assertBlocks(blocks); 25 | assertNotNull(blocks.get(0).toString()); 26 | 27 | if (blocks.size() > 1) { 28 | assertNotEquals(blocks.get(0), blocks.get(1)); 29 | assertNotEquals(blocks.get(0).hashCode(), blocks.get(1).hashCode()); 30 | } 31 | } 32 | 33 | @Test 34 | void invalidParamWithError() { 35 | assertThrows(EtherScanInvalidAddressException.class, 36 | () -> getApi().account().blocksMined("xE4C6175183029A0f039bf2DFffa5C6e8F3cA9B23")); 37 | } 38 | 39 | @Test 40 | void correctParamWithEmptyExpectedResult() { 41 | List txs = api.account().blocksMined("0xE1C6175183029A0f039bf2DFffa5C6e8F3cA9B23"); 42 | assertNotNull(txs); 43 | assertTrue(txs.isEmpty()); 44 | } 45 | 46 | private void assertBlocks(List blocks) { 47 | for (Block block : blocks) { 48 | assertNotEquals(0, block.getBlockNumber()); 49 | assertNotNull(block.getBlockReward()); 50 | assertNotNull(block.getTimeStamp()); 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/account/AccountTokenBalanceTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.account; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.EtherScanAPI; 5 | import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; 6 | import io.goodforgod.api.etherscan.model.TokenBalance; 7 | import org.junit.jupiter.api.Test; 8 | 9 | /** 10 | * @author GoodforGod 11 | * @since 03.11.2018 12 | */ 13 | class AccountTokenBalanceTests extends ApiRunner { 14 | 15 | private final EtherScanAPI api = getApi(); 16 | 17 | @Test 18 | void correct() { 19 | TokenBalance balance = api.account().balance("0x5d807e7F124EC2103a59c5249187f772c0b8D6b2", 20 | "0x5EaC95ad5b287cF44E058dCf694419333b796123"); 21 | assertNotNull(balance); 22 | assertNotNull(balance.getBalanceInWei()); 23 | assertNotNull(balance.getAddress()); 24 | assertNotNull(balance.getContract()); 25 | assertNotNull(balance.toString()); 26 | 27 | TokenBalance balance2 = new TokenBalance("125161", balance.getBalanceInWei(), balance.getContract()); 28 | assertNotEquals(balance, balance2); 29 | assertNotEquals(balance.hashCode(), balance2.hashCode()); 30 | } 31 | 32 | @Test 33 | void invalidAddressParamWithError() { 34 | assertThrows(EtherScanInvalidAddressException.class, 35 | () -> api.account().balance("0x5807e7F124EC2103a59c5249187f772c0b8D6b2", 36 | "0x5EaC95ad5b287cF44E058dCf694419333b796123")); 37 | } 38 | 39 | @Test 40 | void invalidContractParamWithError() { 41 | assertThrows(EtherScanInvalidAddressException.class, 42 | () -> api.account().balance("0x5d807e7F124EC2103a59c5249187f772c0b8D6b2", 43 | "0xEaC95ad5b287cF44E058dCf694419333b796123")); 44 | } 45 | 46 | @Test 47 | void correctParamWithEmptyExpectedResult() { 48 | TokenBalance balance = api.account().balance("0x1d807e7F124EC2103a59c5249187f772c0b8D6b2", 49 | "0x5EaC95ad5b287cF44E058dCf694419333b796123"); 50 | assertNotNull(balance); 51 | assertNotNull(balance.getBalanceInWei()); 52 | assertNotNull(balance.getAddress()); 53 | assertNotNull(balance.getContract()); 54 | assertEquals(0, balance.getBalanceInWei().asWei().intValue()); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/account/AccountTxErc20Tests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.account; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; 5 | import io.goodforgod.api.etherscan.model.TxErc20; 6 | import java.util.List; 7 | import org.junit.jupiter.api.Test; 8 | 9 | /** 10 | * @author GoodforGod 11 | * @since 03.11.2018 12 | */ 13 | class AccountTxErc20Tests extends ApiRunner { 14 | 15 | @Test 16 | void correct() { 17 | List txs = getApi().account().txsErc20("0xE376F69ED2218076682e2b3B7b9099eC50aD68c4"); 18 | assertNotNull(txs); 19 | assertEquals(3, txs.size()); 20 | assertTxs(txs); 21 | assertNotEquals(0, txs.get(0).getGasPrice().asWei().intValue()); 22 | assertNotEquals(-1, txs.get(0).getNonce()); 23 | 24 | assertNotNull(txs.get(0).toString()); 25 | assertNotEquals(txs.get(0).toString(), txs.get(1).toString()); 26 | 27 | assertNotEquals(txs.get(0), txs.get(1)); 28 | assertNotEquals(txs.get(0).hashCode(), txs.get(1).hashCode()); 29 | 30 | assertEquals(txs.get(1), txs.get(1)); 31 | assertEquals(txs.get(1).hashCode(), txs.get(1).hashCode()); 32 | } 33 | 34 | @Test 35 | void correctStartBlock() { 36 | List txs = getApi().account().txsErc20("0x36ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7", 5578167); 37 | assertNotNull(txs); 38 | assertEquals(11, txs.size()); 39 | assertTxs(txs); 40 | } 41 | 42 | @Test 43 | void correctStartBlockEndBlock() { 44 | List txs = getApi().account().txsErc20("0x36ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7", 5578167, 5813576); 45 | assertNotNull(txs); 46 | assertEquals(5, txs.size()); 47 | assertTxs(txs); 48 | } 49 | 50 | @Test 51 | void invalidParamWithError() { 52 | assertThrows(EtherScanInvalidAddressException.class, 53 | () -> getApi().account().txsErc20("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7")); 54 | } 55 | 56 | @Test 57 | void correctParamWithEmptyExpectedResult() { 58 | List txs = getApi().account().txsErc20("0x31ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); 59 | assertNotNull(txs); 60 | assertTrue(txs.isEmpty()); 61 | } 62 | 63 | private void assertTxs(List txs) { 64 | for (TxErc20 tx : txs) { 65 | assertNotNull(tx.getBlockHash()); 66 | assertNotNull(tx.getTokenName()); 67 | assertNotNull(tx.getTokenSymbol()); 68 | assertNotNull(tx.getFrom()); 69 | assertNotNull(tx.getTo()); 70 | assertNotNull(tx.getTimeStamp()); 71 | assertNotNull(tx.getTokenDecimal()); 72 | assertNotEquals(-1, (tx.getConfirmations())); 73 | assertNotNull(tx.getGasUsed()); 74 | assertNotEquals(-1, tx.getGasUsedCumulative().asWei().intValue()); 75 | assertNotEquals(-1, tx.getTransactionIndex()); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalByHashTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.account; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.EtherScanAPI; 5 | import io.goodforgod.api.etherscan.error.EtherScanInvalidTxHashException; 6 | import io.goodforgod.api.etherscan.model.TxInternal; 7 | import io.goodforgod.api.etherscan.util.BasicUtils; 8 | import java.util.List; 9 | import org.junit.jupiter.api.Test; 10 | 11 | /** 12 | * @author GoodforGod 13 | * @since 03.11.2018 14 | */ 15 | class AccountTxInternalByHashTests extends ApiRunner { 16 | 17 | private final EtherScanAPI api = getApi(); 18 | 19 | @Test 20 | void correct() { 21 | List txs = api.account() 22 | .txsInternalByHash("0x1b513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b"); 23 | assertNotNull(txs); 24 | assertEquals(1, txs.size()); 25 | assertTxs(txs); 26 | assertNotNull(txs.get(0).getFrom()); 27 | assertNotNull(txs.get(0).getTimeStamp()); 28 | assertNotNull(txs.get(0).getGas()); 29 | assertNotNull(txs.get(0).getValue()); 30 | assertNotNull(txs.get(0).getType()); 31 | assertFalse(txs.get(0).haveError()); 32 | assertFalse(txs.get(0).haveError()); 33 | assertNotEquals("-1", txs.get(0).getTraceIdAsString()); 34 | assertTrue(BasicUtils.isEmpty(txs.get(0).getErrCode())); 35 | assertNotNull(txs.get(0).toString()); 36 | 37 | if (txs.size() > 9) { 38 | assertNotEquals(txs.get(0), txs.get(9)); 39 | assertNotEquals(txs.get(0).hashCode(), txs.get(9).hashCode()); 40 | } 41 | } 42 | 43 | @Test 44 | void invalidParamWithError() { 45 | assertThrows(EtherScanInvalidTxHashException.class, 46 | () -> api.account().txsInternalByHash("0xb513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b")); 47 | } 48 | 49 | @Test 50 | void correctParamWithEmptyExpectedResult() { 51 | List txs = api.account() 52 | .txsInternalByHash("0x2b513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b"); 53 | assertNotNull(txs); 54 | assertTrue(txs.isEmpty()); 55 | } 56 | 57 | private void assertTxs(List txs) { 58 | for (TxInternal tx : txs) { 59 | assertNotEquals(0, tx.getBlockNumber()); 60 | assertNotNull(tx.getFrom()); 61 | assertNotNull(tx.getTo()); 62 | assertNotNull(tx.getTimeStamp()); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.account; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; 5 | import io.goodforgod.api.etherscan.model.TxInternal; 6 | import java.util.List; 7 | import org.junit.jupiter.api.Test; 8 | 9 | /** 10 | * @author GoodforGod 11 | * @since 03.11.2018 12 | */ 13 | class AccountTxInternalTests extends ApiRunner { 14 | 15 | @Test 16 | void correct() { 17 | List txs = getApi().account().txsInternal("0x2C1ba59D6F58433FB1EaEe7d20b26Ed83bDA51A3"); 18 | assertNotNull(txs); 19 | assertEquals(66, txs.size()); 20 | assertTxs(txs); 21 | assertNotNull(txs.get(0).toString()); 22 | } 23 | 24 | @Test 25 | void correctStartBlock() { 26 | List txs = getApi().account().txsInternal("0x2C1ba59D6F58433FB1EaEe7d20b26Ed83bDA51A3", 2558775); 27 | assertNotNull(txs); 28 | assertEquals(24, txs.size()); 29 | assertNotEquals(txs.get(0), txs.get(1)); 30 | assertNotEquals(txs.get(0).toString(), txs.get(1).toString()); 31 | assertTxs(txs); 32 | } 33 | 34 | @Test 35 | void correctStartBlockEndBlock() { 36 | List txs = getApi().account().txsInternal("0x2C1ba59D6F58433FB1EaEe7d20b26Ed83bDA51A3", 2558775, 2685504); 37 | assertNotNull(txs); 38 | assertEquals(21, txs.size()); 39 | assertTxs(txs); 40 | } 41 | 42 | @Test 43 | void invalidParamWithError() { 44 | assertThrows(EtherScanInvalidAddressException.class, 45 | () -> getApi().account().txsInternal("0x2C1ba59D6F58433FB1EaEe7d20b26Ed83bDA51")); 46 | } 47 | 48 | @Test 49 | void correctParamWithEmptyExpectedResult() { 50 | List txs = getApi().account().txsInternal("0x2C1ba59D6F58433FB2EaEe7d20b26Ed83bDA51A3"); 51 | assertNotNull(txs); 52 | assertTrue(txs.isEmpty()); 53 | } 54 | 55 | private void assertTxs(List txs) { 56 | for (TxInternal tx : txs) { 57 | assertNotNull(tx.getHash()); 58 | assertNotNull(tx.getFrom()); 59 | assertNotNull(tx.getTo()); 60 | assertNotNull(tx.getTimeStamp()); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc1155TokenTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.account; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; 5 | import io.goodforgod.api.etherscan.model.TxErc1155; 6 | import java.util.List; 7 | import org.junit.jupiter.api.Test; 8 | 9 | /** 10 | * @author GoodforGod 11 | * @since 14.05.2023 12 | */ 13 | class AccountTxRc1155TokenTests extends ApiRunner { 14 | 15 | @Test 16 | void correct() { 17 | List txs = getApi().account().txsErc1155("0xE4C8324534C0C6bCA174Cd0F02fAC9889C36bA59"); 18 | assertNotNull(txs); 19 | assertFalse(txs.isEmpty()); 20 | assertTxs(txs); 21 | assertNotEquals(0, txs.get(0).getGasPrice().asWei().intValue()); 22 | assertNotEquals(-1, txs.get(0).getNonce()); 23 | 24 | assertNotNull(txs.get(0).toString()); 25 | assertNotEquals(txs.get(0).toString(), txs.get(1).toString()); 26 | 27 | assertNotEquals(txs.get(0), txs.get(1)); 28 | assertNotEquals(txs.get(0).hashCode(), txs.get(1).hashCode()); 29 | 30 | assertEquals(txs.get(1), txs.get(1)); 31 | assertEquals(txs.get(1).hashCode(), txs.get(1).hashCode()); 32 | } 33 | 34 | @Test 35 | void correctStartBlock() { 36 | List txs = getApi().account().txsErc1155("0xE4C8324534C0C6bCA174Cd0F02fAC9889C36bA59", 14275897); 37 | assertNotNull(txs); 38 | assertFalse(txs.isEmpty()); 39 | assertTxs(txs); 40 | } 41 | 42 | @Test 43 | void correctStartBlockEndBlock() { 44 | List txs = getApi().account().txsErc1155("0xE4C8324534C0C6bCA174Cd0F02fAC9889C36bA59", 14275897, 15148929); 45 | assertNotNull(txs); 46 | assertEquals(11, txs.size()); 47 | assertTxs(txs); 48 | } 49 | 50 | @Test 51 | void invalidParamWithError() { 52 | assertThrows(EtherScanInvalidAddressException.class, 53 | () -> getApi().account().txsErc1155("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7")); 54 | } 55 | 56 | @Test 57 | void correctParamWithEmptyExpectedResult() { 58 | List txs = getApi().account().txsErc1155("0x31ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); 59 | assertNotNull(txs); 60 | assertTrue(txs.isEmpty()); 61 | } 62 | 63 | private void assertTxs(List txs) { 64 | txs.forEach(this::asserTx); 65 | } 66 | 67 | private void asserTx(TxErc1155 tx) { 68 | assertNotNull(tx.getBlockHash()); 69 | assertNotNull(tx.getTokenName()); 70 | assertNotNull(tx.getTokenSymbol()); 71 | assertNotNull(tx.getFrom()); 72 | assertNotNull(tx.getTo()); 73 | assertNotNull(tx.getTimeStamp()); 74 | assertNotNull(tx.getTokenID()); 75 | assertNotNull(tx.getTokenValue()); 76 | assertNotEquals(-1, (tx.getConfirmations())); 77 | assertNotNull(tx.getGasUsed()); 78 | assertNotEquals(-1, tx.getGasUsedCumulative().asWei().intValue()); 79 | assertNotEquals(-1, tx.getTransactionIndex()); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.account; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; 5 | import io.goodforgod.api.etherscan.model.TxErc721; 6 | import java.util.List; 7 | import org.junit.jupiter.api.Test; 8 | 9 | /** 10 | * @author NGuggs 11 | * @since 11.28.2021 12 | */ 13 | class AccountTxRc721TokenTests extends ApiRunner { 14 | 15 | @Test 16 | void correct() { 17 | List txs = getApi().account().txsErc721("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67"); 18 | assertNotNull(txs); 19 | assertEquals(16, txs.size()); 20 | assertTxs(txs); 21 | assertNotEquals(0, txs.get(0).getGasPrice().asWei().intValue()); 22 | assertNotEquals(-1, txs.get(0).getNonce()); 23 | 24 | assertNotNull(txs.get(0).toString()); 25 | assertNotEquals(txs.get(0).toString(), txs.get(1).toString()); 26 | 27 | assertNotEquals(txs.get(0), txs.get(1)); 28 | assertNotEquals(txs.get(0).hashCode(), txs.get(1).hashCode()); 29 | 30 | assertEquals(txs.get(1), txs.get(1)); 31 | assertEquals(txs.get(1).hashCode(), txs.get(1).hashCode()); 32 | } 33 | 34 | @Test 35 | void correctStartBlock() { 36 | List txs = getApi().account().txsErc721("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67", 4762071); 37 | System.out.println(txs); 38 | assertNotNull(txs); 39 | assertEquals(5, txs.size()); 40 | assertTxs(txs); 41 | } 42 | 43 | @Test 44 | void correctStartBlockEndBlock() { 45 | List txs = getApi().account().txsErc721("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67", 4761862, 4761934); 46 | System.out.println(txs); 47 | assertNotNull(txs); 48 | assertEquals(11, txs.size()); 49 | assertTxs(txs); 50 | } 51 | 52 | @Test 53 | void invalidParamWithError() { 54 | assertThrows(EtherScanInvalidAddressException.class, 55 | () -> getApi().account().txsErc721("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7")); 56 | } 57 | 58 | @Test 59 | void correctParamWithEmptyExpectedResult() { 60 | List txs = getApi().account().txsErc721("0x31ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); 61 | assertNotNull(txs); 62 | assertTrue(txs.isEmpty()); 63 | } 64 | 65 | private void assertTxs(List txs) { 66 | for (TxErc721 tx : txs) { 67 | assertNotNull(tx.getBlockHash()); 68 | assertNotNull(tx.getTokenName()); 69 | assertNotNull(tx.getTokenSymbol()); 70 | assertNotNull(tx.getFrom()); 71 | assertNotNull(tx.getTo()); 72 | assertNotNull(tx.getTimeStamp()); 73 | assertNotNull(tx.getTokenDecimal()); 74 | assertNotEquals(-1, (tx.getConfirmations())); 75 | assertNotNull(tx.getGasUsed()); 76 | assertNotEquals(-1, tx.getGasUsedCumulative().asWei().intValue()); 77 | assertNotEquals(-1, tx.getTransactionIndex()); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/account/AccountTxsTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.account; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; 5 | import io.goodforgod.api.etherscan.model.Tx; 6 | import java.util.List; 7 | import org.junit.jupiter.api.Test; 8 | 9 | /** 10 | * @author GoodforGod 11 | * @since 03.11.2018 12 | */ 13 | class AccountTxsTests extends ApiRunner { 14 | 15 | @Test 16 | void correct() { 17 | List txs = getApi().account().txs("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9"); 18 | assertNotNull(txs); 19 | assertEquals(5, txs.size()); 20 | assertTxs(txs); 21 | assertNotNull(txs.get(0).getTimeStamp()); 22 | assertNotNull(txs.get(0).getHash()); 23 | assertNotNull(txs.get(0).getFrom()); 24 | assertNotNull(txs.get(0).getTo()); 25 | assertNotNull(txs.get(0).getBlockHash()); 26 | assertNotNull(txs.get(0).getGas()); 27 | assertNotNull(txs.get(0).getGasUsedCumulative()); 28 | assertNotNull(txs.get(0).getGasPrice()); 29 | assertNotNull(txs.get(0).getValue()); 30 | assertNotNull(txs.get(0).getContractAddress()); 31 | assertNotNull(txs.get(0).getInput()); 32 | assertNotNull(txs.get(0).toString()); 33 | assertNotEquals(txs.get(0), txs.get(1)); 34 | assertNotEquals(txs.get(0).hashCode(), txs.get(1).hashCode()); 35 | assertEquals(txs.get(1), txs.get(1)); 36 | } 37 | 38 | @Test 39 | void correctStartBlock() { 40 | List txs = getApi().account().txs("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9", 3892842); 41 | assertNotNull(txs); 42 | assertEquals(4, txs.size()); 43 | assertTxs(txs); 44 | } 45 | 46 | @Test 47 | void correctStartBlockEndBlock() { 48 | List txs = getApi().account().txs("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9", 3892842, 3945741); 49 | assertNotNull(txs); 50 | assertEquals(3, txs.size()); 51 | assertTxs(txs); 52 | assertNotEquals(txs.get(0), txs.get(1)); 53 | } 54 | 55 | @Test 56 | void invalidParamWithError() { 57 | assertThrows(EtherScanInvalidAddressException.class, 58 | () -> getApi().account().txs("9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9")); 59 | } 60 | 61 | @Test 62 | void correctParamWithEmptyExpectedResult() { 63 | List txs = getApi().account().txs("0x9321cb34984c3992ec1EA0eAE98Ccf80A74f95B9"); 64 | assertNotNull(txs); 65 | assertTrue(txs.isEmpty()); 66 | } 67 | 68 | private void assertTxs(List txs) { 69 | for (Tx tx : txs) { 70 | assertFalse(tx.haveError()); 71 | assertNotNull(tx.getBlockHash()); 72 | assertNotNull(tx.getFrom()); 73 | assertNotNull(tx.getTo()); 74 | assertNotNull(tx.getTimeStamp()); 75 | assertNotEquals(-1, (tx.getNonce())); 76 | assertNotEquals(0, (tx.getTransactionIndex())); 77 | assertNotEquals(0, tx.getConfirmations()); 78 | assertNotNull(tx.getTxReceiptStatus()); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/block/BlockApiTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.block; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.model.BlockUncle; 5 | import java.util.Optional; 6 | import org.junit.jupiter.api.Test; 7 | 8 | /** 9 | * @author GoodforGod 10 | * @since 03.11.2018 11 | */ 12 | class BlockApiTests extends ApiRunner { 13 | 14 | @Test 15 | void correct() { 16 | Optional uncle = getApi().block().uncles(2165403); 17 | assertTrue(uncle.isPresent()); 18 | assertFalse(uncle.get().isEmpty()); 19 | assertNotNull(uncle.get().getBlockMiner()); 20 | assertNotNull(uncle.get().getUncleInclusionReward()); 21 | assertNotNull(uncle.get().getUncles()); 22 | assertFalse(uncle.get().getUncles().isEmpty()); 23 | assertNotNull(uncle.get().getUncles().get(0).getBlockreward()); 24 | assertNotNull(uncle.get().getUncles().get(0).getMiner()); 25 | assertNotEquals(-1, uncle.get().getUncles().get(0).getUnclePosition()); 26 | assertNotNull(uncle.get().toString()); 27 | 28 | BlockUncle empty = BlockUncle.builder().build(); 29 | assertNotEquals(uncle.get().hashCode(), empty.hashCode()); 30 | assertNotEquals(uncle.get(), empty); 31 | assertTrue(empty.isEmpty()); 32 | 33 | if (uncle.get().getUncles().size() > 0) { 34 | assertNotEquals(-1, uncle.get().getUncles().get(0).getUnclePosition()); 35 | assertEquals(uncle.get().getUncles().get(0), uncle.get().getUncles().get(0)); 36 | assertEquals(uncle.get().getUncles().get(0).hashCode(), uncle.get().getUncles().get(0).hashCode()); 37 | } 38 | 39 | if (uncle.get().getUncles().size() > 1) { 40 | assertNotEquals(uncle.get().getUncles().get(1), uncle.get().getUncles().get(0)); 41 | assertNotEquals(uncle.get().getUncles().get(1).hashCode(), uncle.get().getUncles().get(0).hashCode()); 42 | } 43 | } 44 | 45 | @Test 46 | void correctNoUncles() { 47 | Optional uncles = getApi().block().uncles(34); 48 | assertTrue(uncles.isPresent()); 49 | assertTrue(uncles.get().getUncles().isEmpty()); 50 | } 51 | 52 | @Test 53 | void correctParamWithEmptyExpectedResult() { 54 | Optional uncles = getApi().block().uncles(99999999934L); 55 | assertFalse(uncles.isPresent()); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/contract/ContractApiTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.contract; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; 5 | import io.goodforgod.api.etherscan.model.Abi; 6 | import io.goodforgod.api.etherscan.model.ContractCreation; 7 | import java.util.Arrays; 8 | import java.util.Collections; 9 | import java.util.List; 10 | import org.junit.jupiter.api.Test; 11 | 12 | /** 13 | * @author GoodforGod 14 | * @since 03.11.2018 15 | */ 16 | class ContractApiTests extends ApiRunner { 17 | 18 | @Test 19 | void correct() { 20 | Abi abi = getApi().contract().contractAbi("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413"); 21 | assertNotNull(abi); 22 | assertTrue(abi.isVerified()); 23 | assertTrue(abi.haveAbi()); 24 | assertNotNull(abi.getContractAbi()); 25 | assertNotNull(abi.toString()); 26 | 27 | Abi empty = Abi.verified("asg"); 28 | assertNotEquals(empty, abi); 29 | assertNotEquals(empty.hashCode(), abi.hashCode()); 30 | } 31 | 32 | @Test 33 | void invalidParamWithError() { 34 | assertThrows(EtherScanInvalidAddressException.class, 35 | () -> getApi().contract().contractAbi("0xBBbc244D798123fDe783fCc1C72d3Bb8C189413")); 36 | } 37 | 38 | @Test 39 | void correctParamWithEmptyExpectedResult() { 40 | Abi abi = getApi().contract().contractAbi("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413"); 41 | assertNotNull(abi); 42 | assertTrue(abi.isVerified()); 43 | } 44 | 45 | @Test 46 | void correctContractCreation() { 47 | List contractCreations = getApi().contract() 48 | .contractCreation(Collections.singletonList("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413")); 49 | 50 | assertEquals(1, contractCreations.size()); 51 | ContractCreation contractCreation = contractCreations.get(0); 52 | 53 | assertEquals("0xbb9bc244d798123fde783fcc1c72d3bb8c189413", contractCreation.getContractAddress()); 54 | assertEquals("0x793ea9692ada1900fbd0b80fffec6e431fe8b391", contractCreation.getContractCreator()); 55 | assertEquals("0xe9ebfecc2fa10100db51a4408d18193b3ac504584b51a4e55bdef1318f0a30f9", contractCreation.getTxHash()); 56 | } 57 | 58 | @Test 59 | void correctMultipleContractCreation() { 60 | List contractCreations = getApi().contract().contractCreation( 61 | Arrays.asList("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413", "0x5EaC95ad5b287cF44E058dCf694419333b796123")); 62 | assertEquals(2, contractCreations.size()); 63 | 64 | ContractCreation contractCreation1 = ContractCreation.builder() 65 | .withContractAddress("0xbb9bc244d798123fde783fcc1c72d3bb8c189413") 66 | .withContractCreator("0x793ea9692ada1900fbd0b80fffec6e431fe8b391") 67 | .withTxHash("0xe9ebfecc2fa10100db51a4408d18193b3ac504584b51a4e55bdef1318f0a30f9") 68 | .build(); 69 | 70 | ContractCreation contractCreation2 = ContractCreation.builder() 71 | .withContractAddress("0x5eac95ad5b287cf44e058dcf694419333b796123") 72 | .withContractCreator("0x7c675b7450e878e5af8550b41df42d134674e61f") 73 | .withTxHash("0x79cdfec19e5a86d9022680a4d1c86d3d8cd76c21c01903a2f02c127a0a7dbfb3") 74 | .build(); 75 | 76 | assertTrue(contractCreations.contains(contractCreation1)); 77 | assertTrue(contractCreations.contains(contractCreation2)); 78 | } 79 | 80 | @Test 81 | void contractCreationInvalidParamWithError() { 82 | assertThrows(EtherScanInvalidAddressException.class, 83 | () -> getApi().contract() 84 | .contractCreation(Collections.singletonList("0xBBbc244D798123fDe783fCc1C72d3Bb8C189414"))); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/gastracker/GasTrackerApiTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.gastracker; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.model.GasOracle; 5 | import io.goodforgod.api.etherscan.model.Wei; 6 | import java.time.Duration; 7 | import org.junit.jupiter.api.Test; 8 | 9 | /** 10 | * @author GoodforGod 11 | * @since 14.05.2023 12 | */ 13 | class GasTrackerApiTests extends ApiRunner { 14 | 15 | @Test 16 | void estimate() { 17 | Duration estimate = getApi().gasTracker().estimate(Wei.ofWei(123)); 18 | assertNotNull(estimate); 19 | } 20 | 21 | @Test 22 | void oracle() { 23 | GasOracle oracle = getApi().gasTracker().oracle(); 24 | assertNotNull(oracle); 25 | assertNotNull(oracle.getGasUsedRatio()); 26 | assertNotNull(oracle.getFastGasPriceInWei()); 27 | assertNotNull(oracle.getLastBlock()); 28 | assertNotNull(oracle.getProposeGasPriceInWei()); 29 | assertNotNull(oracle.getSafeGasPriceInWei()); 30 | assertNotNull(oracle.getSuggestBaseFee()); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/logs/LogsApiTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.logs; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.model.Log; 5 | import io.goodforgod.api.etherscan.model.query.LogOp; 6 | import io.goodforgod.api.etherscan.model.query.LogQuery; 7 | import java.util.List; 8 | import java.util.stream.Stream; 9 | import org.junit.jupiter.params.ParameterizedTest; 10 | import org.junit.jupiter.params.provider.Arguments; 11 | import org.junit.jupiter.params.provider.MethodSource; 12 | 13 | /** 14 | * @author GoodforGod 15 | * @since 03.11.2018 16 | */ 17 | class LogsApiTests extends ApiRunner { 18 | 19 | static Stream source() { 20 | LogQuery single = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") 21 | .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") 22 | .build(); 23 | 24 | LogQuery singleInvalidAddr = LogQuery.builder("0x13990122638b9132ca29c723bdf037f1a891a70c") 25 | .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") 26 | .build(); 27 | 28 | LogQuery tupleAnd = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c").withBlockFrom(379224) 29 | .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", 30 | "0x72657075746174696f6e00000000000000000000000000000000000000000000") 31 | .setOpTopic0_1(LogOp.AND) 32 | .build(); 33 | 34 | LogQuery tupleOr = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") 35 | .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", 36 | "0x72657075746174696f6e00000000000000000000000000000000000000000000") 37 | .setOpTopic0_1(LogOp.OR) 38 | .build(); 39 | 40 | return Stream.of( 41 | Arguments.of(single, 424), 42 | Arguments.of(singleInvalidAddr, 0), 43 | Arguments.of(tupleAnd, 1), 44 | Arguments.of(tupleOr, 426)); 45 | } 46 | 47 | @ParameterizedTest 48 | @MethodSource("source") 49 | void validateQuery(LogQuery query, int logsSize) { 50 | List logs = getApi().logs().logs(query); 51 | assertEquals(logsSize, logs.size()); 52 | 53 | if (logsSize > 0) { 54 | if (logsSize > 1) { 55 | assertNotEquals(logs.get(0), logs.get(1)); 56 | assertNotEquals(logs.get(0).hashCode(), logs.get(1).hashCode()); 57 | } 58 | 59 | assertNotNull(logs.get(0).getAddress()); 60 | assertNotNull(logs.get(0).getBlockNumber()); 61 | assertNotNull(logs.get(0).getData()); 62 | assertNotNull(logs.get(0).getTimeStamp()); 63 | assertNotNull(logs.get(0).getTransactionHash()); 64 | assertNotNull(logs.get(0).getTransactionIndex()); 65 | assertNotNull(logs.get(0).getGasUsed()); 66 | assertNotNull(logs.get(0).getTopics()); 67 | assertNotNull(logs.get(0).getLogIndex()); 68 | assertNotNull(logs.get(0).getGasPrice()); 69 | assertNotNull(logs.get(0).toString()); 70 | 71 | assertEquals(logs.get(0), logs.get(0)); 72 | assertEquals(logs.get(0).hashCode(), logs.get(0).hashCode()); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/manager/SemaphoreRequestQueueManagerTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.manager; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.manager.impl.FakeRequestQueueManager; 5 | import io.goodforgod.api.etherscan.manager.impl.SemaphoreRequestQueueManager; 6 | import java.time.Duration; 7 | import org.junit.jupiter.api.Test; 8 | import org.junit.jupiter.api.Timeout; 9 | 10 | /** 11 | * @author GoodforGod 12 | * @since 03.11.2018 13 | */ 14 | class SemaphoreRequestQueueManagerTests extends ApiRunner { 15 | 16 | @Test 17 | void fakeManager() { 18 | RequestQueueManager fakeManager = new FakeRequestQueueManager(); 19 | fakeManager.takeTurn(); 20 | fakeManager.takeTurn(); 21 | fakeManager.takeTurn(); 22 | fakeManager.takeTurn(); 23 | fakeManager.takeTurn(); 24 | fakeManager.takeTurn(); 25 | assertNotNull(fakeManager); 26 | } 27 | 28 | @Test 29 | @Timeout(3500) 30 | void queueManager() { 31 | RequestQueueManager requestQueueManager = new SemaphoreRequestQueueManager(1, Duration.ofSeconds(3)); 32 | requestQueueManager.takeTurn(); 33 | requestQueueManager.takeTurn(); 34 | assertNotNull(requestQueueManager); 35 | } 36 | 37 | @Test 38 | @Timeout(4500) 39 | void queueManagerWithDelay() { 40 | RequestQueueManager requestQueueManager = new SemaphoreRequestQueueManager(1, Duration.ofSeconds(2)); 41 | requestQueueManager.takeTurn(); 42 | requestQueueManager.takeTurn(); 43 | assertNotNull(requestQueueManager); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.proxy; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.model.proxy.BlockProxy; 5 | import java.util.Optional; 6 | import org.junit.jupiter.api.Test; 7 | 8 | /** 9 | * @author GoodforGod 10 | * @since 03.11.2018 11 | */ 12 | class ProxyBlockApiTests extends ApiRunner { 13 | 14 | @Test 15 | void correct() { 16 | Optional block = getApi().proxy().block(5120); 17 | assertTrue(block.isPresent()); 18 | BlockProxy proxy = block.get(); 19 | assertNotNull(proxy.getHash()); 20 | assertNotNull(proxy.getNumber()); 21 | assertNotNull(proxy.getParentHash()); 22 | assertNotNull(proxy.getStateRoot()); 23 | assertNotNull(proxy.getSize()); 24 | assertNotNull(proxy.getDifficulty()); 25 | assertNotNull(proxy.getTotalDifficulty()); 26 | assertNotNull(proxy.getTimeStamp()); 27 | assertNotNull(proxy.getMiner()); 28 | assertNotNull(proxy.getNonce()); 29 | assertNotNull(proxy.getHash()); 30 | assertNotNull(proxy.getExtraData()); 31 | assertNotNull(proxy.getLogsBloom()); 32 | assertNotNull(proxy.getMixHash()); 33 | assertNotNull(proxy.getGasUsed()); 34 | assertNotNull(proxy.getGasLimit()); 35 | assertNotNull(proxy.getSha3Uncles()); 36 | assertNotNull(proxy.getTransactions()); 37 | assertNotNull(proxy.getTransactionsRoot()); 38 | assertNotNull(proxy.getReceiptsRoot()); 39 | assertNotNull(proxy.getUncles()); 40 | assertNotNull(proxy.toString()); 41 | 42 | BlockProxy empty = BlockProxy.builder().build(); 43 | assertNotEquals(proxy, empty); 44 | assertNotEquals(proxy.hashCode(), empty.hashCode()); 45 | } 46 | 47 | @Test 48 | void correctParamWithEmptyExpectedResult() { 49 | Optional block = getApi().proxy().block(99999999999L); 50 | assertFalse(block.isPresent()); 51 | } 52 | 53 | @Test 54 | void correctParamNegativeNo() { 55 | Optional block = getApi().proxy().block(-1); 56 | assertTrue(block.isPresent()); 57 | assertNotNull(block.get().getHash()); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockLastNoApiTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.proxy; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import org.junit.jupiter.api.Test; 5 | 6 | /** 7 | * @author GoodforGod 8 | * @since 13.11.2018 9 | */ 10 | class ProxyBlockLastNoApiTests extends ApiRunner { 11 | 12 | @Test 13 | void correct() { 14 | long noLast = getApi().proxy().blockNoLast(); 15 | assertNotEquals(0, noLast); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockUncleApiTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.proxy; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.model.proxy.BlockProxy; 5 | import java.util.Optional; 6 | import org.junit.jupiter.api.Test; 7 | 8 | /** 9 | * @author GoodforGod 10 | * @since 13.11.2018 11 | */ 12 | class ProxyBlockUncleApiTests extends ApiRunner { 13 | 14 | @Test 15 | void correct() { 16 | Optional block = getApi().proxy().blockUncle(603183, 0); 17 | assertTrue(block.isPresent()); 18 | assertNotNull(block.get().getHash()); 19 | assertNotNull(block.get().toString()); 20 | } 21 | 22 | @Test 23 | void correctParamWithEmptyExpectedResult() { 24 | Optional block = getApi().proxy().blockUncle(5120, 1); 25 | assertFalse(block.isPresent()); 26 | } 27 | 28 | @Test 29 | void correctParamNegativeNo() { 30 | Optional block = getApi().proxy().blockUncle(-603183, 0); 31 | assertFalse(block.isPresent()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCallApiTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.proxy; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; 5 | import io.goodforgod.api.etherscan.error.EtherScanInvalidDataHexException; 6 | import io.goodforgod.api.etherscan.util.BasicUtils; 7 | import java.util.Optional; 8 | import org.junit.jupiter.api.Test; 9 | 10 | /** 11 | * @author GoodforGod 12 | * @since 03.11.2018 13 | */ 14 | class ProxyCallApiTests extends ApiRunner { 15 | 16 | @Test 17 | void correct() { 18 | Optional call = getApi().proxy().call("0xAEEF46DB4855E25702F8237E8f403FddcaF931C0", 19 | "0x70a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724"); 20 | assertTrue(call.isPresent()); 21 | assertFalse(BasicUtils.isNotHex(call.get())); 22 | } 23 | 24 | @Test 25 | void invalidParamWithError() { 26 | assertThrows(EtherScanInvalidAddressException.class, 27 | () -> getApi().proxy().call("0xEEF46DB4855E25702F8237E8f403FddcaF931C0", 28 | "0x70a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724")); 29 | } 30 | 31 | @Test 32 | void invalidParamNotHex() { 33 | assertThrows(EtherScanInvalidDataHexException.class, 34 | () -> getApi().proxy().call("0xAEEF46DB4855E25702F8237E8f403FddcaF931C0", 35 | "7-0a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724")); 36 | } 37 | 38 | @Test 39 | void correctParamWithEmptyExpectedResult() { 40 | Optional call = getApi().proxy().call("0xAEEF16DB4855E25702F8237E8f403FddcaF931C0", 41 | "0x70a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724"); 42 | assertTrue(call.isPresent()); 43 | assertFalse(BasicUtils.isNotHex(call.get()), call.get()); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCodeApiTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.proxy; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; 5 | import io.goodforgod.api.etherscan.util.BasicUtils; 6 | import java.util.Optional; 7 | import org.junit.jupiter.api.Test; 8 | 9 | /** 10 | * @author GoodforGod 11 | * @since 03.11.2018 12 | */ 13 | class ProxyCodeApiTests extends ApiRunner { 14 | 15 | @Test 16 | void correct() { 17 | Optional call = getApi().proxy().code("0xf75e354c5edc8efed9b59ee9f67a80845ade7d0c"); 18 | assertTrue(call.isPresent()); 19 | assertFalse(BasicUtils.isNotHex(call.get()), call.get()); 20 | } 21 | 22 | @Test 23 | void invalidParamWithError() { 24 | assertThrows(EtherScanInvalidAddressException.class, 25 | () -> getApi().proxy().code("0f75e354c5edc8efed9b59ee9f67a80845ade7d0c")); 26 | } 27 | 28 | @Test 29 | void correctParamWithEmptyExpectedResult() { 30 | Optional call = getApi().proxy().code("0xf15e354c5edc8efed9b59ee9f67a80845ade7d0c"); 31 | assertTrue(call.isPresent()); 32 | assertFalse(BasicUtils.isNotHex(call.get()), call.get()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/proxy/ProxyGasApiTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.proxy; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.error.EtherScanInvalidDataHexException; 5 | import io.goodforgod.api.etherscan.model.Wei; 6 | import org.junit.jupiter.api.Test; 7 | 8 | /** 9 | * @author GoodforGod 10 | * @since 03.11.2018 11 | */ 12 | class ProxyGasApiTests extends ApiRunner { 13 | 14 | @Test 15 | void correctPrice() { 16 | Wei price = getApi().proxy().gasPrice(); 17 | assertNotNull(price); 18 | assertNotEquals(0, price.asWei().intValue()); 19 | } 20 | 21 | @Test 22 | void correctEstimated() { 23 | Wei price = getApi().proxy().gasEstimated(); 24 | assertNotNull(price); 25 | assertNotEquals(0, price.asWei().intValue()); 26 | } 27 | 28 | @Test 29 | void correctEstimatedWithData() { 30 | String dataCustom = "606060405260728060106000396000f360606040526000606060405260728060106000396000f360606040526000"; 31 | Wei price = getApi().proxy().gasEstimated(); 32 | Wei priceCustom = getApi().proxy().gasEstimated(dataCustom); 33 | assertNotNull(price); 34 | assertNotNull(priceCustom); 35 | assertNotEquals(price, priceCustom); 36 | } 37 | 38 | @Test 39 | void invalidParamWithError() { 40 | String dataCustom = "280&60106000396000f360606040526000"; 41 | assertThrows(EtherScanInvalidDataHexException.class, () -> getApi().proxy().gasEstimated(dataCustom)); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/proxy/ProxyStorageApiTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.proxy; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; 5 | import java.util.Optional; 6 | import org.junit.jupiter.api.Test; 7 | 8 | /** 9 | * @author GoodforGod 10 | * @since 03.11.2018 11 | */ 12 | class ProxyStorageApiTests extends ApiRunner { 13 | 14 | @Test 15 | void correct() { 16 | Optional call = getApi().proxy().storageAt("0x6e03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 0); 17 | assertFalse(call.isPresent()); 18 | } 19 | 20 | @Test 21 | void invalidParamWithError() { 22 | assertThrows(EtherScanInvalidAddressException.class, 23 | () -> getApi().proxy().storageAt("0xe03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 0)); 24 | } 25 | 26 | @Test 27 | void correctParamWithEmptyExpectedResult() { 28 | final Optional call = getApi().proxy().storageAt("0x6e03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 10000); 29 | assertFalse(call.isPresent()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxApiTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.proxy; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.error.EtherScanInvalidTxHashException; 5 | import io.goodforgod.api.etherscan.model.proxy.TxProxy; 6 | import java.util.Optional; 7 | import org.junit.jupiter.api.Test; 8 | 9 | /** 10 | * @author GoodforGod 11 | * @since 03.11.2018 12 | */ 13 | class ProxyTxApiTests extends ApiRunner { 14 | 15 | @Test 16 | void correctByHash() { 17 | Optional tx = getApi().proxy().tx("0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); 18 | assertTrue(tx.isPresent()); 19 | assertNotNull(tx.get().getBlockHash()); 20 | assertNotNull(tx.get().getFrom()); 21 | assertNotNull(tx.get().getTo()); 22 | assertNotNull(tx.get().getHash()); 23 | assertNotNull(tx.get().getNonce()); 24 | assertNotNull(tx.get().getBlockNumber()); 25 | assertNotNull(tx.get().toString()); 26 | 27 | TxProxy empty = TxProxy.builder().build(); 28 | assertNotEquals(tx.get(), empty); 29 | assertNotEquals(tx.get().hashCode(), empty.hashCode()); 30 | } 31 | 32 | @Test 33 | void correctByBlockNo() { 34 | Optional tx = getApi().proxy().tx(637368, 0); 35 | assertTrue(tx.isPresent()); 36 | assertNotNull(tx.get().getBlockHash()); 37 | assertNotNull(tx.get().getFrom()); 38 | assertNotNull(tx.get().getTo()); 39 | assertNotNull(tx.get().getHash()); 40 | assertNotNull(tx.get().getNonce()); 41 | assertNotNull(tx.get().getS()); 42 | assertNotNull(tx.get().getR()); 43 | assertNotNull(tx.get().getValue()); 44 | assertNotNull(tx.get().getV()); 45 | assertNotNull(tx.get().getGas()); 46 | assertNotNull(tx.get().getGasPrice()); 47 | assertNotNull(tx.get().getBlockHash()); 48 | assertNotNull(tx.get().getTransactionIndex()); 49 | assertNotNull(tx.get().getInput()); 50 | } 51 | 52 | @Test 53 | void invalidParamWithError() { 54 | assertThrows(EtherScanInvalidTxHashException.class, 55 | () -> getApi().proxy().tx("0xe2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1")); 56 | } 57 | 58 | @Test 59 | void correctParamWithEmptyExpectedResultBlockNoExist() { 60 | Optional tx = getApi().proxy().tx(99999999L, 0); 61 | assertFalse(tx.isPresent()); 62 | } 63 | 64 | @Test 65 | void correctParamWithEmptyExpectedResult() { 66 | Optional tx = getApi().proxy().tx("0x2e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); 67 | assertFalse(tx.isPresent()); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxCountApiTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.proxy; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; 5 | import org.junit.jupiter.api.Test; 6 | 7 | /** 8 | * @author GoodforGod 9 | * @since 03.11.2018 10 | */ 11 | class ProxyTxCountApiTests extends ApiRunner { 12 | 13 | @Test 14 | void correctSended() { 15 | int count = getApi().proxy().txSendCount("0x2910543af39aba0cd09dbb2d50200b3e800a63d2"); 16 | assertNotEquals(0, count); 17 | } 18 | 19 | @Test 20 | void correctByBlockNo() { 21 | int count = getApi().proxy().txCount(6137420); 22 | assertNotEquals(0, count); 23 | } 24 | 25 | @Test 26 | void invalidParamWithError() { 27 | assertThrows(EtherScanInvalidAddressException.class, 28 | () -> getApi().proxy().txSendCount("0xe03d9cce9d60f3e9f2597e13cd4c54c55330cfd")); 29 | } 30 | 31 | @Test 32 | void correctParamWithEmptyExpectedResultBlockNoExist() { 33 | int count = getApi().proxy().txCount(99999999999L); 34 | assertNotEquals(1, count); 35 | } 36 | 37 | @Test 38 | void correctParamWithEmptyExpectedResult() { 39 | int count = getApi().proxy().txSendCount("0x1e03d9cce9d60f3e9f2597e13cd4c54c55330cfd"); 40 | assertNotEquals(1, count); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxReceiptApiTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.proxy; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.error.EtherScanInvalidTxHashException; 5 | import io.goodforgod.api.etherscan.model.proxy.ReceiptProxy; 6 | import java.util.Optional; 7 | import org.junit.jupiter.api.Test; 8 | 9 | /** 10 | * @author GoodforGod 11 | * @since 03.11.2018 12 | */ 13 | class ProxyTxReceiptApiTests extends ApiRunner { 14 | 15 | @Test 16 | void correct() { 17 | Optional infoProxy = getApi().proxy() 18 | .txReceipt("0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); 19 | assertTrue(infoProxy.isPresent()); 20 | assertNotNull(infoProxy.get().getBlockHash()); 21 | assertNotNull(infoProxy.get().getRoot()); 22 | assertNotNull(infoProxy.get().getFrom()); 23 | assertNotNull(infoProxy.get().getTo()); 24 | assertNotNull(infoProxy.get().getBlockNumber()); 25 | assertNotNull(infoProxy.get().getBlockHash()); 26 | assertNotNull(infoProxy.get().getTransactionHash()); 27 | assertNotNull(infoProxy.get().getTransactionIndex()); 28 | assertNotNull(infoProxy.get().getGasUsed()); 29 | assertNotNull(infoProxy.get().getGasUsedCumulative()); 30 | assertNotNull(infoProxy.get().getLogs()); 31 | assertNotNull(infoProxy.get().getLogsBloom()); 32 | assertNull(infoProxy.get().getContractAddress()); 33 | assertNotNull(infoProxy.get().toString()); 34 | 35 | ReceiptProxy empty = ReceiptProxy.builder().build(); 36 | assertNotEquals(empty, infoProxy.get()); 37 | assertNotEquals(empty.hashCode(), infoProxy.get().hashCode()); 38 | } 39 | 40 | @Test 41 | void invalidParamWithError() { 42 | assertThrows(EtherScanInvalidTxHashException.class, () -> getApi().proxy() 43 | .txReceipt("0xe2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1")); 44 | } 45 | 46 | @Test 47 | void correctParamWithEmptyExpectedResult() { 48 | Optional infoProxy = getApi().proxy() 49 | .txReceipt("0x2e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); 50 | assertFalse(infoProxy.isPresent()); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxSendRawApiTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.proxy; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.error.EtherScanInvalidDataHexException; 5 | import io.goodforgod.api.etherscan.error.EtherScanResponseException; 6 | import java.util.Optional; 7 | import org.junit.jupiter.api.Test; 8 | 9 | /** 10 | * @author GoodforGod 11 | * @since 03.11.2018 12 | */ 13 | // TODO contact etherscan and ask about method behavior 14 | class ProxyTxSendRawApiTests extends ApiRunner { 15 | 16 | void correct() { 17 | Optional sendRaw = getApi().proxy() 18 | .txSendRaw("0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); 19 | assertTrue(sendRaw.isPresent()); 20 | } 21 | 22 | @Test 23 | void invalidParamWithError() { 24 | assertThrows(EtherScanInvalidDataHexException.class, () -> getApi().proxy().txSendRaw("5151=0561")); 25 | } 26 | 27 | @Test 28 | void invalidParamEtherScanDataException() { 29 | assertThrows(EtherScanResponseException.class, () -> getApi().proxy().txSendRaw("0x1")); 30 | } 31 | 32 | void correctParamWithEmptyExpectedResult() { 33 | Optional sendRaw = getApi().proxy().txSendRaw("0x000000"); 34 | assertFalse(sendRaw.isPresent()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.statistic; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.model.Price; 5 | import org.junit.jupiter.api.Test; 6 | 7 | /** 8 | * @author GoodforGod 9 | * @since 03.11.2018 10 | */ 11 | class StatisticPriceApiTests extends ApiRunner { 12 | 13 | @Test 14 | void correct() { 15 | Price price = getApi().stats().priceLast(); 16 | assertNotNull(price); 17 | assertNotNull(price.timestampBtc()); 18 | assertNotNull(price.timestampUsd()); 19 | assertNotEquals(0.0, price.inBtc().doubleValue()); 20 | assertNotEquals(0.0, price.inUsd().doubleValue()); 21 | assertNotNull(price.toString()); 22 | 23 | Price empty = Price.builder().build(); 24 | assertNotEquals(price, empty); 25 | assertNotEquals(price.hashCode(), empty.hashCode()); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyApiTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.statistic; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.model.Wei; 5 | import java.math.BigInteger; 6 | import org.junit.jupiter.api.Test; 7 | 8 | /** 9 | * @author GoodforGod 10 | * @since 03.11.2018 11 | */ 12 | class StatisticSupplyApiTests extends ApiRunner { 13 | 14 | @Test 15 | void correct() { 16 | Wei supply = getApi().stats().supply(); 17 | assertNotNull(supply); 18 | assertNotNull(supply.asWei()); 19 | assertNotNull(supply.asGwei()); 20 | assertNotNull(supply.asKwei()); 21 | assertNotNull(supply.asMwei()); 22 | assertNotNull(supply.asEther()); 23 | assertNotNull(supply.toString()); 24 | 25 | Wei empty = Wei.ofWei(BigInteger.ONE); 26 | assertNotEquals(supply, empty); 27 | assertNotEquals(supply.hashCode(), empty.hashCode()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyTotalApiTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.statistic; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.model.EthSupply; 5 | import org.junit.jupiter.api.Test; 6 | 7 | /** 8 | * @author GoodforGod 9 | * @since 14.05.2023 10 | */ 11 | class StatisticSupplyTotalApiTests extends ApiRunner { 12 | 13 | @Test 14 | void correct() { 15 | EthSupply supply = getApi().stats().supplyTotal(); 16 | assertNotNull(supply); 17 | assertNotNull(supply.getBurntFees()); 18 | assertNotEquals(0, supply.getBurntFees().asWei().intValue()); 19 | assertNotNull(supply.getEthSupply()); 20 | assertNotEquals(0, supply.getEthSupply().asWei().intValue()); 21 | assertNotNull(supply.getEth2Staking()); 22 | assertNotEquals(0, supply.getEth2Staking().asWei().intValue()); 23 | assertNotNull(supply.getWithdrawnTotal()); 24 | assertNotEquals(0, supply.getWithdrawnTotal().asWei().intValue()); 25 | assertNotNull(supply.getTotal()); 26 | assertNotEquals(0, supply.getTotal().asWei().intValue()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/statistic/StatisticTokenSupplyApiTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.statistic; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; 5 | import io.goodforgod.api.etherscan.model.Wei; 6 | import java.math.BigInteger; 7 | import org.junit.jupiter.api.Test; 8 | 9 | /** 10 | * @author GoodforGod 11 | * @since 03.11.2018 12 | */ 13 | class StatisticTokenSupplyApiTests extends ApiRunner { 14 | 15 | @Test 16 | void correct() { 17 | Wei supply = getApi().stats().supply("0x57d90b64a1a57749b0f932f1a3395792e12e7055"); 18 | assertNotNull(supply); 19 | assertNotEquals(BigInteger.ZERO, supply.asWei()); 20 | } 21 | 22 | @Test 23 | void invalidParamWithError() { 24 | assertThrows(EtherScanInvalidAddressException.class, 25 | () -> getApi().stats().supply("0x7d90b64a1a57749b0f932f1a3395792e12e7055")); 26 | } 27 | 28 | @Test 29 | void correctParamWithEmptyExpectedResult() { 30 | Wei supply = getApi().stats().supply("0x51d90b64a1a57749b0f932f1a3395792e12e7055"); 31 | assertNotNull(supply); 32 | assertEquals(0, supply.asEther().intValue()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/support/AddressUtil.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.support; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.concurrent.ThreadLocalRandom; 6 | 7 | /** 8 | * @author GoodforGod 9 | * @since 03.11.2018 10 | */ 11 | public class AddressUtil { 12 | 13 | static List genFakeAddresses(int size) { 14 | final List addresses = new ArrayList<>(); 15 | for (int i = 0; i < size; i++) 16 | addresses.add("0x9327cb34984c" + ThreadLocalRandom.current().nextInt(1000, 9999) + "ec1EA0eAE98Ccf80A74f95B9"); 17 | 18 | return addresses; 19 | } 20 | 21 | public static List genRealAddresses() { 22 | final List addresses = new ArrayList<>(); 23 | 24 | addresses.add("0x00e01a648ff41346cdeb873182383333d2184dd1"); 25 | addresses.add("0x6b92ab6d455d06b022bf5003922cdd8b07172586"); 26 | addresses.add("0x69c46c1e40465ae9a6c90990ae1568005fe62d6e"); 27 | addresses.add("0x199eee5b6ca1aaf030c77f0b5c50e39908fd2072"); 28 | addresses.add("0x9327cb34984c3992ec1ea0eae98ccf80a74f95b9"); 29 | addresses.add("0xc9f32ce1127e44c51cbd182d6364f3d707fd0d47"); 30 | addresses.add("0x122c7f492c51c247e293b0f996fa63de61474959"); 31 | addresses.add("0xdb82af76f9ccddfe9e8f7996492f4c5bd5f9d53d"); 32 | addresses.add("0xfbb62df5cbc8d0bc4c2a0e5c53b8602d49471495"); 33 | addresses.add("0xa7b8edb0583b478e2b7c431feb4bc6629a6cd1e5"); 34 | addresses.add("0xa8afa9a714e06b9eb1b500617494ac213f2d428a"); 35 | addresses.add("0x4985a8D068A9CAC4B81FaaAFB8D4516AE46dD768"); 36 | addresses.add("0x581ffAadEEB6d183A73d93413e137CB8c6057603"); 37 | addresses.add("0x66a6892477F9B48Ed47f407B30ddE754405E1910"); 38 | addresses.add("0xD4718F4601Af5365360896F763bb9D7173257450"); 39 | addresses.add("0x05fBf1E3f105df6a4553f3C7f2ed93070A4BAB46"); 40 | addresses.add("0x36ec53a8fba6358d59b3c4476d82cc60a2b0fad7"); 41 | addresses.add("0x7a16a0fd441a7591f2456c3cda068e33c6bbaa96"); 42 | addresses.add("0xe22D388301E7D57e20F59E03c14CA4c026C6C3d9"); 43 | addresses.add("0x7f55B3b6774b9573aE2c36eA790118CeBa6C057F"); 44 | addresses.add("0x2965deab20b16565d5fb08799728c51b6cdda34b"); 45 | addresses.add("0xad2e834840954231b924b51bfb92c7f457ca30d9"); 46 | addresses.add("0x5fE584cB2d2A937C4AbBD0cad57841cc8Db97Df1"); 47 | addresses.add("0x536de519a2ab6fd0cc8055e5119b3c2a8dd2464b"); 48 | addresses.add("0xe546f64ebf75cdc134e2e2a05fd6ed2fbfbf7c8d"); 49 | addresses.add("0x9b398c9d1b4516b9c3ccbc4c6b6350702ed58c4d"); 50 | 51 | return addresses; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/transaction/TransactionExecApiTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.transaction; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.error.EtherScanInvalidTxHashException; 5 | import io.goodforgod.api.etherscan.model.Status; 6 | import java.util.Optional; 7 | import org.junit.jupiter.api.Test; 8 | 9 | /** 10 | * @author GoodforGod 11 | * @since 03.11.2018 12 | */ 13 | class TransactionExecApiTests extends ApiRunner { 14 | 15 | @Test 16 | void correct() { 17 | Optional status = getApi().txs().statusExec("0x15f8e5ea1079d9a0bb04a4c58ae5fe7654b5b2b4463375ff7ffb490aa0032f3a"); 18 | assertTrue(status.isPresent()); 19 | assertTrue(status.get().haveError()); 20 | assertNotNull(status.get().getErrDescription()); 21 | assertNotNull(status.get().toString()); 22 | 23 | Status empty = Status.builder().build(); 24 | assertNotEquals(empty, status.get()); 25 | assertNotEquals(empty.hashCode(), status.get().hashCode()); 26 | } 27 | 28 | @Test 29 | void invalidParamWithError() { 30 | assertThrows(EtherScanInvalidTxHashException.class, 31 | () -> getApi().txs().statusExec("0xb513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b")); 32 | } 33 | 34 | @Test 35 | void correctParamWithEmptyExpectedResult() { 36 | Optional status = getApi().txs().statusExec("0x55f8e5ea1079d9a0bb04a4c58ae5fe7654b5b2b4463375ff7ffb490aa0032f3a"); 37 | assertTrue(status.isPresent()); 38 | assertFalse(status.get().haveError()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/transaction/TransactionReceiptApiTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.transaction; 2 | 3 | import io.goodforgod.api.etherscan.ApiRunner; 4 | import io.goodforgod.api.etherscan.error.EtherScanInvalidTxHashException; 5 | import java.util.Optional; 6 | import org.junit.jupiter.api.Test; 7 | 8 | /** 9 | * @author GoodforGod 10 | * @since 03.11.2018 11 | */ 12 | class TransactionReceiptApiTests extends ApiRunner { 13 | 14 | @Test 15 | void correct() { 16 | Optional status = getApi().txs() 17 | .statusReceipt("0x513c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76"); 18 | assertTrue(status.isPresent()); 19 | assertTrue(status.get()); 20 | } 21 | 22 | @Test 23 | void invalidParamWithError() { 24 | assertThrows(EtherScanInvalidTxHashException.class, 25 | () -> getApi().txs().statusReceipt("0x13c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76")); 26 | } 27 | 28 | @Test 29 | void correctParamWithEmptyExpectedResult() { 30 | Optional status = getApi().txs() 31 | .statusReceipt("0x113c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76"); 32 | assertFalse(status.isPresent()); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/test/java/io/goodforgod/api/etherscan/util/BasicUtilsTests.java: -------------------------------------------------------------------------------- 1 | package io.goodforgod.api.etherscan.util; 2 | 3 | import static io.goodforgod.api.etherscan.util.BasicUtils.*; 4 | 5 | import com.google.gson.Gson; 6 | import io.goodforgod.api.etherscan.ApiRunner; 7 | import io.goodforgod.api.etherscan.error.EtherScanResponseException; 8 | import io.goodforgod.api.etherscan.model.response.StringResponseTO; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | import org.junit.jupiter.api.Test; 12 | 13 | /** 14 | * @author GoodforGod 15 | * @since 13.11.2018 16 | */ 17 | class BasicUtilsTests extends ApiRunner { 18 | 19 | @Test 20 | void responseValidateEmpty() { 21 | String response = "{\"status\":\"0\",\"message\":\"No ether\",\"result\":\"status\"}"; 22 | StringResponseTO responseTO = new Gson().fromJson(response, StringResponseTO.class); 23 | 24 | assertThrows(EtherScanResponseException.class, () -> validateTxResponse(responseTO)); 25 | } 26 | 27 | @Test 28 | void partitionEmpty() { 29 | ArrayList list = new ArrayList<>(); 30 | List> lists = partition(list, 12); 31 | assertTrue(lists.isEmpty()); 32 | } 33 | 34 | @Test 35 | void partitionNullParam() { 36 | List> lists = partition(null, 12); 37 | assertTrue(lists.isEmpty()); 38 | } 39 | 40 | @Test 41 | void isBlankNull() { 42 | boolean result = isBlank(null); 43 | assertTrue(result); 44 | } 45 | 46 | @Test 47 | void isEmptyCollectionEmpty() { 48 | ArrayList list = new ArrayList<>(); 49 | boolean result = isEmpty(list); 50 | assertTrue(result); 51 | } 52 | 53 | @Test 54 | void isNotAddressNull() { 55 | boolean result = isNotAddress(""); 56 | assertTrue(result); 57 | } 58 | 59 | @Test 60 | void isNotHexNull() { 61 | boolean result = isNotHex(""); 62 | assertTrue(result); 63 | } 64 | 65 | @Test 66 | void isNotAddressInvalid() { 67 | boolean result = isNotAddress("125125"); 68 | assertTrue(result); 69 | } 70 | 71 | @Test 72 | void isNotHexInvalid() { 73 | boolean result = isNotHex("1215%"); 74 | assertTrue(result); 75 | } 76 | 77 | @Test 78 | void isResponseStatusInvalidThrows() { 79 | StringResponseTO responseTO = new StringResponseTO(); 80 | assertThrows(EtherScanResponseException.class, () -> validateTxResponse(responseTO)); 81 | } 82 | 83 | @Test 84 | void isResponseNullThrows() { 85 | StringResponseTO responseTO = null; 86 | assertThrows(EtherScanResponseException.class, () -> validateTxResponse(responseTO)); 87 | } 88 | } 89 | --------------------------------------------------------------------------------