diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 03c2e11..613a39e 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -4,12 +4,10 @@ on: push: branches: - master - schedule: - - cron: "0 12 1 * *" pull_request: branches: - - master - - dev + - master + - dev jobs: build: @@ -20,21 +18,26 @@ jobs: name: Java ${{ matrix.java }} setup steps: - - uses: actions/checkout@v1 - - name: Set up JDK - uses: actions/setup-java@v1 - - with: - java-version: ${{ matrix.java }} - - - name: Build with Gradle - run: ./gradlew build jacocoTestReport - env: - API_KEY: ${{ secrets.API_KEY }} - - - name: Analyze with SonarQube - run: ./gradlew sonarqube - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - API_KEY: ${{ secrets.API_KEY }} + - uses: actions/checkout@v1 + - name: Set up JDK + uses: actions/setup-java@v1 + + with: + java-version: ${{ matrix.java }} + + - name: Build + run: ./gradlew classes + + - name: Codestyle + run: ./gradlew spotlessCheck + + - name: Test + run: ./gradlew test jacocoTestReport + env: + API_KEY: ${{ secrets.API_KEY }} + + - name: SonarQube + run: ./gradlew sonarqube + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/README.md b/README.md index 352952b..cd88aaa 100644 --- a/README.md +++ b/README.md @@ -11,22 +11,23 @@ Library supports all available EtherScan *API* calls for all available *Ethereum Networks* for *etherscan.io* ## Dependency :rocket: + +**Gradle** +```groovy +dependencies { + compile "com.github.goodforgod:java-etherscan-api:1.1.1" +} +``` + **Maven** ```xml com.github.goodforgod java-etherscan-api - 1.1.0 + 1.1.1 ``` -**Gradle** -```groovy -dependencies { - compile 'com.github.goodforgod:java-etherscan-api:1.1.0' -} -``` - ## Content - [Ethereum Networks](#mainnet-and-testnets) - [Custom HttpClient](#custom-httpclient) @@ -42,6 +43,7 @@ dependencies { - [Version History](#version-history) ## Mainnet and Testnets + API support Ethereum: *[MAINNET](https://etherscan.io), [ROPSTEN](https://ropsten.etherscan.io), [KOVAN](https://kovan.etherscan.io), @@ -88,14 +90,18 @@ EtherScanApi api = new EtherScanApi("YourApiKey"); Below are examples for each API category. ### Account Api + **Get Ether Balance for a single Address** + ```java EtherScanApi api = new EtherScanApi(); Balance balance = api.account().balance("0x8d4426f94e42f721C7116E81d6688cd935cB3b4F"); ``` ### Block Api + **Get uncles block for block height** + ```java EtherScanApi api = new EtherScanApi(); Optional uncles = api.block().uncles(200000); @@ -109,7 +115,9 @@ Abi abi = api.contract().contractAbi("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413 ``` ### Logs Api + **Get event logs for single topic** + ```java EtherScanApi api = new EtherScanApi(); LogQuery query = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") @@ -119,6 +127,7 @@ List logs = api.logs().logs(query); ``` **Get event logs for 3 topics with respectful operations** + ```java EtherScanApi api = new EtherScanApi(); LogQuery query = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) @@ -134,47 +143,45 @@ List logs = api.logs().logs(query); ``` ### Proxy Api + **Get tx detailds with proxy endpoint** + ```java EtherScanApi api = new EtherScanApi(EthNetwork.MAINNET); Optional tx = api.proxy().tx("0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); ``` **Get block info with proxy endpoint** + ```java EtherScanApi api = new EtherScanApi(EthNetwork.MAINNET); Optional block = api.proxy().block(15215); ``` ### Stats Api + **Statistic about last price** + ```java EtherScanApi api = new EtherScanApi(); Price price = api.stats().lastPrice(); ``` ### Transaction Api + **Request receipt status for tx** + ```java EtherScanApi api = new EtherScanApi(); Optional status = api.txs().receiptStatus("0x513c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76"); ``` ### Token Api + You can read about token API [here](https://etherscan.io/apis#tokens) Token API methods migrated to [Account](#account-api) & [Stats](#stats-api) respectfully. -## Version History - -**1.1.0** - Improved error handling, QueueManager improved, Gradle 6.7 instead of Maven, GitHub CI, Sonarcloud analyzer, dependencies updated. - -**1.0.2** - Minor http client improvements. - -**1.0.1** - Gorli & TOBALABA networks support. - -**1.0.0** - Initial project with all API functionality, for all available networks, with tests coverage for all cases. - ## License This project licensed under the MIT - see the [LICENSE](LICENSE) file for details. diff --git a/build.gradle b/build.gradle index 638baf3..0e3f321 100644 --- a/build.gradle +++ b/build.gradle @@ -1,27 +1,26 @@ plugins { - id 'jacoco' - id 'java-library' - id 'maven-publish' + id "jacoco" + id "java-library" + id "maven-publish" - id 'org.sonarqube' version '3.1.1' - id 'com.diffplug.spotless' version '5.11.0' + id "org.sonarqube" version "3.3" + id "com.diffplug.spotless" version "5.14.3" } repositories { mavenLocal() mavenCentral() - jcenter() } group = groupId version = artifactVersion -sourceCompatibility = 1.8 -targetCompatibility = 1.8 +sourceCompatibility = JavaVersion.VERSION_1_8 +targetCompatibility = JavaVersion.VERSION_1_8 spotless { java { - encoding 'UTF-8' + encoding "UTF-8" removeUnusedImports() eclipse().configFile "${projectDir}/config/codestyle.xml" } @@ -29,43 +28,30 @@ spotless { sonarqube { properties { - property 'sonar.host.url', 'https://sonarcloud.io' - property 'sonar.organization', 'goodforgod' - property 'sonar.projectKey', 'GoodforGod_java-etherscan-api' + property "sonar.host.url", "https://sonarcloud.io" + property "sonar.organization", "goodforgod" + property "sonar.projectKey", "GoodforGod_java-etherscan-api" } } dependencies { - implementation 'org.jetbrains:annotations:20.1.0' - implementation 'com.google.code.gson:gson:2.8.6' + implementation "org.jetbrains:annotations:22.0.0" + implementation "com.google.code.gson:gson:2.8.8" - testImplementation 'junit:junit:4.13.1' + testImplementation "junit:junit:4.13.1" } test { - failFast = true - useJUnit() testLogging { - events "passed", "skipped", "failed" - exceptionFormat "full" + events("passed", "skipped", "failed") + exceptionFormat("full") } -} -tasks.withType(JavaCompile) { - options.encoding = 'UTF-8' - options.incremental = true - options.fork = true -} - -tasks.withType(Test) { - reports.html.enabled = false - reports.junitXml.enabled = false -} - -java { - withJavadocJar() - withSourcesJar() + reports { + html.enabled(false) + junitXml.enabled(false) + } } publishing { @@ -74,27 +60,27 @@ publishing { from components.java pom { - name = 'Java Etherscan API' - url = 'https://github.com/GoodforGod/java-etherscan-api' - description = 'Library is a wrapper for EtherScan API.' + name = "Java Etherscan API" + url = "https://github.com/GoodforGod/java-etherscan-api" + description = "Library is a wrapper for EtherScan API." license { - name = 'MIT License' - url = 'https://github.com/GoodforGod/java-etherscan-api/blob/master/LICENSE' - distribution = 'repo' + name = "MIT License" + url = "https://github.com/GoodforGod/java-etherscan-api/blob/master/LICENSE" + distribution = "repo" } developer { - id = 'GoodforGod' - name = 'Anton Kurako' - email = 'goodforgod.dev@gmail.com' - url = 'https://github.com/GoodforGod' + id = "GoodforGod" + name = "Anton Kurako" + email = "goodforgod.dev@gmail.com" + url = "https://github.com/GoodforGod" } scm { - connection = 'scm:git:git://github.com/GoodforGod/java-etherscan-api.git' - developerConnection = 'scm:git:ssh://GoodforGod/java-etherscan-api.git' - url = 'https://github.com/GoodforGod/java-etherscan-api/tree/master' + connection = "scm:git:git://github.com/GoodforGod/java-etherscan-api.git" + developerConnection = "scm:git:ssh://GoodforGod/java-etherscan-api.git" + url = "https://github.com/GoodforGod/java-etherscan-api/tree/master" } } } @@ -103,7 +89,7 @@ publishing { maven { def releasesRepoUrl = "https://oss.sonatype.org/service/local/staging/deploy/maven2" def snapshotsRepoUrl = "https://oss.sonatype.org/content/repositories/snapshots/" - url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl + url = version.endsWith("SNAPSHOT") ? snapshotsRepoUrl : releasesRepoUrl credentials { username System.getenv("OSS_USERNAME") password System.getenv("OSS_PASSWORD") @@ -112,6 +98,17 @@ publishing { } } +java { + withJavadocJar() + withSourcesJar() +} + +tasks.withType(JavaCompile) { + options.encoding("UTF-8") + options.incremental(true) + options.fork = true +} + check.dependsOn jacocoTestReport jacocoTestReport { reports { @@ -120,16 +117,16 @@ jacocoTestReport { } } +javadoc { + options.encoding = "UTF-8" + if (JavaVersion.current().isJava9Compatible()) { + options.addBooleanOption("html5", true) + } +} + if (project.hasProperty("signing.keyId")) { - apply plugin: 'signing' + apply plugin: "signing" signing { sign publishing.publications.mavenJava } } - -javadoc { - options.encoding = "UTF-8" - if (JavaVersion.current().isJava9Compatible()) { - options.addBooleanOption('html5', true) - } -} \ No newline at end of file diff --git a/config/codestyle.xml b/config/codestyle.xml index 0c19beb..a90c4f5 100644 --- a/config/codestyle.xml +++ b/config/codestyle.xml @@ -74,7 +74,7 @@ - + diff --git a/gradle.properties b/gradle.properties index 1fa1ad4..e4aacfe 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ groupId=com.github.goodforgod artifactId=java-etherscan-api -artifactVersion=1.1.0 +artifactVersion=1.1.1 buildNumber=1 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e708b1c..7454180 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index be52383..ffed3a2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 4f906e0..744e882 100755 --- a/gradlew +++ b/gradlew @@ -72,7 +72,7 @@ case "`uname`" in Darwin* ) darwin=true ;; - MINGW* ) + MSYS* | MINGW* ) msys=true ;; NONSTOP* ) diff --git a/src/main/java/io/api/etherscan/core/IAccountApi.java b/src/main/java/io/api/etherscan/core/IAccountApi.java index 6eda869..642f289 100644 --- a/src/main/java/io/api/etherscan/core/IAccountApi.java +++ b/src/main/java/io/api/etherscan/core/IAccountApi.java @@ -36,8 +36,8 @@ public interface IAccountApi { TokenBalance balance(String address, String contract) throws ApiException; /** - * Maximum 20 address for single batch request If address MORE THAN 20, then - * there will be more than 1 request performed + * Maximum 20 address for single batch request If address MORE THAN 20, then there will be more than + * 1 request performed * * @param addresses addresses to get balances for * @return list of balances diff --git a/src/main/java/io/api/etherscan/core/ILogsApi.java b/src/main/java/io/api/etherscan/core/ILogsApi.java index dc7d8d8..37c5eac 100644 --- a/src/main/java/io/api/etherscan/core/ILogsApi.java +++ b/src/main/java/io/api/etherscan/core/ILogsApi.java @@ -16,8 +16,7 @@ public interface ILogsApi { /** - * alternative to the native eth_getLogs Read at EtherScan API description for - * full info! + * alternative to the native eth_getLogs Read at EtherScan API description for full info! * * @param query build log query * @return logs according to query diff --git a/src/main/java/io/api/etherscan/core/IProxyApi.java b/src/main/java/io/api/etherscan/core/IProxyApi.java index 58a70ed..e57f6ec 100644 --- a/src/main/java/io/api/etherscan/core/IProxyApi.java +++ b/src/main/java/io/api/etherscan/core/IProxyApi.java @@ -36,8 +36,7 @@ public interface IProxyApi { Optional block(long blockNo) throws ApiException; /** - * Returns information about a uncle by block number - * eth_getUncleByBlockNumberAndIndex + * Returns information about a uncle by block number eth_getUncleByBlockNumberAndIndex * * @param blockNo block number from 0 to last * @param index uncle block index @@ -59,8 +58,8 @@ public interface IProxyApi { Optional tx(String txhash) throws ApiException; /** - * Returns information about a transaction by block number and transaction index - * position eth_getTransactionByBlockNumberAndIndex + * Returns information about a transaction by block number and transaction index position + * eth_getTransactionByBlockNumberAndIndex * * @param blockNo block number from 0 to last * @param index tx index in block @@ -71,8 +70,8 @@ public interface IProxyApi { Optional tx(long blockNo, long index) throws ApiException; /** - * Returns the number of transactions in a block from a block matching the given - * block number eth_getBlockTransactionCountByNumber + * Returns the number of transactions in a block from a block matching the given block number + * eth_getBlockTransactionCountByNumber * * @param blockNo block number from 0 to last * @return transaction amount in block @@ -81,8 +80,7 @@ public interface IProxyApi { int txCount(long blockNo) throws ApiException; /** - * Returns the number of transactions sent from an address - * eth_getTransactionCount + * Returns the number of transactions sent from an address eth_getTransactionCount * * @param address eth address * @return transactions send amount from address @@ -91,8 +89,8 @@ public interface IProxyApi { int txSendCount(String address) throws ApiException; /** - * Creates new message call transaction or a contract creation for signed - * transactions eth_sendRawTransaction + * Creates new message call transaction or a contract creation for signed transactions + * eth_sendRawTransaction * * @param hexEncodedTx encoded hex data to send * @return optional string response @@ -102,8 +100,7 @@ public interface IProxyApi { Optional txSendRaw(String hexEncodedTx) throws ApiException; /** - * Returns the receipt of a transaction by transaction hash - * eth_getTransactionReceipt + * Returns the receipt of a transaction by transaction hash eth_getTransactionReceipt * * @param txhash transaction hash * @return optional tx receipt @@ -113,8 +110,8 @@ public interface IProxyApi { Optional txReceipt(String txhash) throws ApiException; /** - * Executes a new message call immediately without creating a transaction on the - * block chain eth_call + * Executes a new message call immediately without creating a transaction on the block chain + * eth_call * * @param address to call * @param data data to call address @@ -135,8 +132,7 @@ public interface IProxyApi { Optional code(String address) throws ApiException; /** - * (**experimental) Returns the value from a storage position at a given address - * eth_getStorageAt + * (**experimental) Returns the value from a storage position at a given address eth_getStorageAt * * @param address to get storage * @param position storage position @@ -156,9 +152,8 @@ public interface IProxyApi { BigInteger gasPrice() throws ApiException; /** - * Makes a call or transaction, which won't be added to the blockchain and - * returns the used gas, which can be used for estimating the used gas - * eth_estimateGas + * Makes a call or transaction, which won't be added to the blockchain and returns the used gas, + * which can be used for estimating the used gas eth_estimateGas * * @param hexData data to calc gas usage for * @return estimated gas usage diff --git a/src/main/java/io/api/etherscan/core/ITransactionApi.java b/src/main/java/io/api/etherscan/core/ITransactionApi.java index 51c108c..f545c2d 100644 --- a/src/main/java/io/api/etherscan/core/ITransactionApi.java +++ b/src/main/java/io/api/etherscan/core/ITransactionApi.java @@ -15,8 +15,7 @@ public interface ITransactionApi { /** - * Check Contract Execution Status (if there was an error during contract - * execution) + * Check Contract Execution Status (if there was an error during contract execution) * * @param txhash transaction hash * @return optional status result @@ -26,8 +25,7 @@ public interface ITransactionApi { Optional execStatus(String txhash) throws ApiException; /** - * Check Transaction Receipt Status (Only applicable for Post Byzantium fork - * transactions) + * Check Transaction Receipt Status (Only applicable for Post Byzantium fork transactions) * * @param txhash transaction hash * @return 0 = Fail, 1 = Pass, empty value for pre-byzantium fork diff --git a/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java b/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java index 8cd5d46..ba5dd83 100644 --- a/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java +++ b/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java @@ -20,12 +20,13 @@ * @author GoodforGod * @since 28.10.2018 */ -public class EtherScanApi { +public class EtherScanApi implements AutoCloseable { private static final Supplier DEFAULT_SUPPLIER = HttpExecutor::new; public static final String DEFAULT_KEY = "YourApiKeyToken"; + private final IQueueManager queueManager; private final IAccountApi account; private final IBlockApi block; private final IContractApi contract; @@ -87,6 +88,7 @@ public EtherScanApi(final String apiKey, final String ending = EthNetwork.TOBALABA.equals(network) ? "com" : "io"; final String baseUrl = "https://" + network.getDomain() + ".etherscan." + ending + "/api" + "?apikey=" + apiKey; + this.queueManager = queue; this.account = new AccountApiProvider(queue, baseUrl, executor); this.block = new BlockApiProvider(queue, baseUrl, executor); this.contract = new ContractApiProvider(queue, baseUrl, executor); @@ -130,4 +132,9 @@ public IProxyApi proxy() { public IStatisticApi stats() { return stats; } + + @Override + public void close() throws Exception { + queueManager.close(); + } } diff --git a/src/main/java/io/api/etherscan/manager/IQueueManager.java b/src/main/java/io/api/etherscan/manager/IQueueManager.java index 3a65240..98a3172 100644 --- a/src/main/java/io/api/etherscan/manager/IQueueManager.java +++ b/src/main/java/io/api/etherscan/manager/IQueueManager.java @@ -1,13 +1,13 @@ package io.api.etherscan.manager; /** - * Queue manager to support API limits (EtherScan 5request\sec limit) Managers - * grants turn if the limit is not exhausted And resets queue each set period + * Queue manager to support API limits (EtherScan 5request\sec limit) Managers grants turn if the + * limit is not exhausted And resets queue each set period * * @author GoodforGod * @since 30.10.2018 */ -public interface IQueueManager { +public interface IQueueManager extends AutoCloseable { /** * Waits in queue for chance to take turn diff --git a/src/main/java/io/api/etherscan/manager/impl/FakeQueueManager.java b/src/main/java/io/api/etherscan/manager/impl/FakeQueueManager.java index e797f8b..620244c 100644 --- a/src/main/java/io/api/etherscan/manager/impl/FakeQueueManager.java +++ b/src/main/java/io/api/etherscan/manager/impl/FakeQueueManager.java @@ -15,4 +15,9 @@ public void takeTurn() { // no limit or await provided for fake impl so rate limit exception will be // thrown if too many calls } + + @Override + public void close() { + // do nothing + } } diff --git a/src/main/java/io/api/etherscan/manager/impl/QueueManager.java b/src/main/java/io/api/etherscan/manager/impl/QueueManager.java index 517883c..80f7c51 100644 --- a/src/main/java/io/api/etherscan/manager/impl/QueueManager.java +++ b/src/main/java/io/api/etherscan/manager/impl/QueueManager.java @@ -12,29 +12,53 @@ * @author GoodforGod * @since 30.10.2018 */ -public class QueueManager implements IQueueManager { +public class QueueManager implements IQueueManager, AutoCloseable { public static final QueueManager DEFAULT_KEY_QUEUE = new QueueManager(1, 7); - public static final QueueManager PERSONAL_KEY_QUEUE = new QueueManager(2, 1); + public static final QueueManager PERSONAL_KEY_QUEUE = new QueueManager(5, 1100L, 1100L, 5); + private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); private final Semaphore semaphore; + private final long queueResetTimeInMillis; public QueueManager(int size, int resetInSec) { this(size, resetInSec, resetInSec); } public QueueManager(int size, int queueResetTimeInSec, int delayInSec) { - this.semaphore = new Semaphore(size); - Executors.newSingleThreadScheduledExecutor() - .scheduleAtFixedRate(releaseLocks(size), delayInSec, queueResetTimeInSec, TimeUnit.SECONDS); + this(size, queueResetTimeInSec, delayInSec, size); } + public QueueManager(int size, int queueResetTimeInSec, int delayInSec, int initialSize) { + this(size, + (long) queueResetTimeInSec * 1000, + (long) delayInSec * 1000, + initialSize); + } + + public QueueManager(int size, long queueResetTimeInMillis, long delayInMillis, int initialSize) { + this.queueResetTimeInMillis = queueResetTimeInMillis; + this.semaphore = new Semaphore(initialSize); + this.executorService.scheduleAtFixedRate(releaseLocks(size), delayInMillis, queueResetTimeInMillis, + TimeUnit.MILLISECONDS); + } + + @SuppressWarnings("java:S899") @Override public void takeTurn() { - semaphore.acquireUninterruptibly(); + try { + semaphore.tryAcquire(queueResetTimeInMillis, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } } private Runnable releaseLocks(int toRelease) { return () -> semaphore.release(toRelease); } + + @Override + public void close() { + executorService.shutdown(); + } } diff --git a/src/main/java/io/api/etherscan/model/Log.java b/src/main/java/io/api/etherscan/model/Log.java index 4649d6b..36d126b 100644 --- a/src/main/java/io/api/etherscan/model/Log.java +++ b/src/main/java/io/api/etherscan/model/Log.java @@ -68,11 +68,10 @@ public LocalDateTime getTimeStamp() { } /** - * Return the "timeStamp" field of the event record as a long-int representing - * the milliseconds since the Unix epoch (1970-01-01 00:00:00). + * Return the "timeStamp" field of the event record as a long-int representing the milliseconds + * since the Unix epoch (1970-01-01 00:00:00). * - * @return milliseconds between Unix epoch and `timeStamp`. If field is empty or - * null, returns null + * @return milliseconds between Unix epoch and `timeStamp`. If field is empty or null, returns null */ public Long getTimeStampAsMillis() { if (BasicUtils.isEmpty(timeStamp)) { diff --git a/src/test/java/io/api/ApiRunner.java b/src/test/java/io/api/ApiRunner.java index 6f608d8..5ccdf83 100644 --- a/src/test/java/io/api/ApiRunner.java +++ b/src/test/java/io/api/ApiRunner.java @@ -3,6 +3,7 @@ import io.api.etherscan.core.impl.EtherScanApi; import io.api.etherscan.manager.impl.QueueManager; import io.api.etherscan.model.EthNetwork; +import org.junit.AfterClass; import org.junit.Assert; public class ApiRunner extends Assert { @@ -11,26 +12,23 @@ public class ApiRunner extends Assert { private static final EtherScanApi apiRopsten; private static final EtherScanApi apiRinkeby; private static final EtherScanApi apiKovan; - private static final String key; + private static final String apiKey; static { - final String apiKey = System.getenv("API_KEY"); - key = (apiKey == null || apiKey.isEmpty()) + final String key = System.getenv("API_KEY"); + apiKey = (key == null || key.isEmpty()) ? EtherScanApi.DEFAULT_KEY - : apiKey; + : key; - final QueueManager queue = key.equals(EtherScanApi.DEFAULT_KEY) - ? QueueManager.DEFAULT_KEY_QUEUE - : new QueueManager(1, 2); - - api = new EtherScanApi(key, EthNetwork.MAINNET, queue); - apiRopsten = new EtherScanApi(key, EthNetwork.ROPSTEN, queue); - apiRinkeby = new EtherScanApi(key, EthNetwork.RINKEBY, queue); - apiKovan = new EtherScanApi(key, EthNetwork.KOVAN, queue); + final QueueManager queueManager = new QueueManager(1, 1200L, 1200L, 0); + api = new EtherScanApi(ApiRunner.apiKey, EthNetwork.MAINNET, queueManager); + apiKovan = new EtherScanApi(ApiRunner.apiKey, EthNetwork.KOVAN, queueManager); + apiRopsten = new EtherScanApi(ApiRunner.apiKey, EthNetwork.ROPSTEN, queueManager); + apiRinkeby = new EtherScanApi(ApiRunner.apiKey, EthNetwork.RINKEBY, queueManager); } - public static String getKey() { - return key; + public static String getApiKey() { + return apiKey; } public static EtherScanApi getApi() { @@ -48,4 +46,12 @@ public static EtherScanApi getApiRinkeby() { public static EtherScanApi getApiKovan() { return apiKovan; } + + @AfterClass + public static void cleanup() throws Exception { + api.close(); + apiRopsten.close(); + apiRinkeby.close(); + apiKovan.close(); + } } diff --git a/src/test/java/io/api/etherscan/EtherScanApiTest.java b/src/test/java/io/api/etherscan/EtherScanApiTest.java index 5071a68..be49435 100644 --- a/src/test/java/io/api/etherscan/EtherScanApiTest.java +++ b/src/test/java/io/api/etherscan/EtherScanApiTest.java @@ -79,7 +79,7 @@ public void noTimeoutUnlimitedAwait() { public void timeout() throws InterruptedException { TimeUnit.SECONDS.sleep(5); Supplier supplier = () -> new HttpExecutor(300, 300); - EtherScanApi api = new EtherScanApi(getKey(), EthNetwork.KOVAN, supplier); + EtherScanApi api = new EtherScanApi(getApiKey(), EthNetwork.KOVAN, supplier); List blocks = api.account().minedBlocks("0x0010f94b296A852aAac52EA6c5Ac72e03afD032D"); assertNotNull(blocks); } diff --git a/src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java index 181a68d..5d3884d 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java @@ -1,6 +1,9 @@ package io.api.etherscan.proxy; import io.api.ApiRunner; +import io.api.etherscan.core.impl.EtherScanApi; +import io.api.etherscan.manager.impl.QueueManager; +import io.api.etherscan.model.EthNetwork; import io.api.etherscan.model.proxy.BlockProxy; import org.junit.Test; @@ -14,9 +17,16 @@ */ public class ProxyBlockApiTest extends ApiRunner { + private final EtherScanApi api; + + public ProxyBlockApiTest() { + final QueueManager queueManager = new QueueManager(1, 5100L, 5100L, 0); + this.api = new EtherScanApi(getApiKey(), EthNetwork.MAINNET, queueManager); + } + @Test public void correct() { - Optional block = getApi().proxy().block(5120); + Optional block = api.proxy().block(5120); assertTrue(block.isPresent()); BlockProxy proxy = block.get(); assertNotNull(proxy.getHash()); @@ -49,13 +59,13 @@ public void correct() { @Test public void correctParamWithEmptyExpectedResult() { - Optional block = getApi().proxy().block(99999999999L); + Optional block = api.proxy().block(99999999999L); assertFalse(block.isPresent()); } @Test public void correctParamNegativeNo() { - Optional block = getApi().proxy().block(-1); + Optional block = api.proxy().block(-1); assertTrue(block.isPresent()); assertNotNull(block.get().getHash()); } diff --git a/src/test/java/io/api/etherscan/proxy/ProxyCallApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyCallApiTest.java index c90850c..07d26bd 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyCallApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyCallApiTest.java @@ -41,6 +41,6 @@ public void correctParamWithEmptyExpectedResult() { Optional call = getApi().proxy().call("0xAEEF16DB4855E25702F8237E8f403FddcaF931C0", "0x70a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724"); assertTrue(call.isPresent()); - assertFalse(BasicUtils.isNotHex(call.get())); + assertFalse(call.get(), BasicUtils.isNotHex(call.get())); } } diff --git a/src/test/java/io/api/etherscan/proxy/ProxyCodeApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyCodeApiTest.java index 3120503..9e4910c 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyCodeApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyCodeApiTest.java @@ -19,18 +19,18 @@ public class ProxyCodeApiTest extends ApiRunner { public void correct() { Optional call = getApi().proxy().code("0xf75e354c5edc8efed9b59ee9f67a80845ade7d0c"); assertTrue(call.isPresent()); - assertFalse(BasicUtils.isNotHex(call.get())); + assertFalse(call.get(), BasicUtils.isNotHex(call.get())); } @Test(expected = InvalidAddressException.class) public void invalidParamWithError() { - Optional call = getApi().proxy().code("0f75e354c5edc8efed9b59ee9f67a80845ade7d0c"); + getApi().proxy().code("0f75e354c5edc8efed9b59ee9f67a80845ade7d0c"); } @Test public void correctParamWithEmptyExpectedResult() { Optional call = getApi().proxy().code("0xf15e354c5edc8efed9b59ee9f67a80845ade7d0c"); assertTrue(call.isPresent()); - assertFalse(BasicUtils.isNotHex(call.get())); + assertFalse(call.get(), BasicUtils.isNotHex(call.get())); } } diff --git a/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java index fbfcf80..ecd7dca 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java @@ -24,12 +24,12 @@ public void correct() { @Test(expected = InvalidAddressException.class) public void invalidParamWithError() { - Optional call = getApi().proxy().storageAt("0xe03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 0); + getApi().proxy().storageAt("0xe03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 0); } @Test public void correctParamWithEmptyExpectedResult() { - Optional call = getApi().proxy().storageAt("0x6e03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 100); + final Optional call = getApi().proxy().storageAt("0x6e03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 10000); assertFalse(call.isPresent()); } } diff --git a/src/test/java/io/api/etherscan/proxy/ProxyTxCountApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyTxCountApiTest.java index 6a0778c..b81926f 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyTxCountApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyTxCountApiTest.java @@ -32,12 +32,12 @@ public void invalidParamWithError() { @Test public void correctParamWithEmptyExpectedResultBlockNoExist() { int count = getApi().proxy().txCount(99999999999L); - assertEquals(0, count); + assertNotEquals(1, count); } @Test public void correctParamWithEmptyExpectedResult() { int count = getApi().proxy().txSendCount("0x1e03d9cce9d60f3e9f2597e13cd4c54c55330cfd"); - assertEquals(0, count); + assertNotEquals(1, count); } }