Skip to content

Commit

Permalink
[2.1.0-SNAPSHOT]
Browse files Browse the repository at this point in the history
EtherScanAPI.Builder#withRetryOnLimitReach added
  • Loading branch information
GoodforGod committed Oct 5, 2023
1 parent 3b1af9d commit 3a87ca8
Show file tree
Hide file tree
Showing 33 changed files with 242 additions and 175 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
groupId=com.github.goodforgod
artifactId=java-etherscan-api
artifactVersion=2.0.0
artifactVersion=2.1.0-SNAPSHOT


##### GRADLE #####
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,9 @@ final class AccountAPIProvider extends BasicProvider implements AccountAPI {
AccountAPIProvider(RequestQueueManager requestQueueManager,
String baseUrl,
EthHttpClient executor,
Converter converter) {
super(requestQueueManager, "account", baseUrl, executor, converter);
Converter converter,
int retryCount) {
super(requestQueueManager, "account", baseUrl, executor, converter, retryCount);
}

@NotNull
Expand Down
51 changes: 45 additions & 6 deletions src/main/java/io/goodforgod/api/etherscan/BasicProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,23 @@ abstract class BasicProvider {
private final EthHttpClient executor;
private final RequestQueueManager queue;
private final Converter converter;
private final int retryCountLimit;

BasicProvider(RequestQueueManager requestQueueManager,
String module,
String baseUrl,
EthHttpClient ethHttpClient,
Converter converter) {
Converter converter,
int retryCountLimit) {
this.queue = requestQueueManager;
this.module = "&module=" + module;
this.baseUrl = baseUrl;
this.executor = ethHttpClient;
this.converter = converter;
this.retryCountLimit = retryCountLimit;
}

<T> T convert(byte[] json, Class<T> tClass) {
private <T> T convert(byte[] json, Class<T> tClass) {
try {
final T t = converter.fromJson(json, tClass);
if (t instanceof StringResponseTO && ((StringResponseTO) t).getResult().startsWith(MAX_RATE_LIMIT_REACHED)) {
Expand All @@ -66,23 +69,59 @@ <T> T convert(byte[] json, Class<T> tClass) {
}
}

byte[] getRequest(String urlParameters) {
private byte[] getRequest(String urlParameters) {
queue.takeTurn();
final URI uri = URI.create(baseUrl + module + urlParameters);
return executor.get(uri);
}

byte[] postRequest(String urlParameters, String dataToPost) {
private byte[] postRequest(String urlParameters, String dataToPost) {
queue.takeTurn();
final URI uri = URI.create(baseUrl + module + urlParameters);
return executor.post(uri, dataToPost.getBytes(StandardCharsets.UTF_8));
}

<T> T getRequest(String urlParameters, Class<T> tClass) {
return convert(getRequest(urlParameters), tClass);
return getRequest(urlParameters, tClass, 0);
}

private <T> T getRequest(String urlParameters, Class<T> tClass, int retryCount) {
try {
return convert(getRequest(urlParameters), tClass);
} catch (Exception e) {
if (retryCount < retryCountLimit) {
try {
Thread.sleep(1150);
} catch (InterruptedException ex) {
throw new IllegalStateException(ex);
}

return getRequest(urlParameters, tClass, retryCount + 1);
} else {
throw e;
}
}
}

<T> T postRequest(String urlParameters, String dataToPost, Class<T> tClass) {
return convert(postRequest(urlParameters, dataToPost), tClass);
return postRequest(urlParameters, dataToPost, tClass, 0);
}

private <T> T postRequest(String urlParameters, String dataToPost, Class<T> tClass, int retryCount) {
try {
return convert(postRequest(urlParameters, dataToPost), tClass);
} catch (EtherScanRateLimitException e) {
if (retryCount < retryCountLimit) {
try {
Thread.sleep(1150);
} catch (InterruptedException ex) {
throw new IllegalStateException(ex);
}

return postRequest(urlParameters, dataToPost, tClass, retryCount + 1);
} else {
throw e;
}
}
}
}
12 changes: 4 additions & 8 deletions src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,17 @@ final class BlockAPIProvider extends BasicProvider implements BlockAPI {
BlockAPIProvider(RequestQueueManager requestQueueManager,
String baseUrl,
EthHttpClient executor,
Converter converter) {
super(requestQueueManager, "block", baseUrl, executor, converter);
Converter converter,
int retryCount) {
super(requestQueueManager, "block", baseUrl, executor, converter, retryCount);
}

@NotNull
@Override
public Optional<BlockUncle> uncles(long blockNumber) throws EtherScanException {
final String urlParam = ACT_BLOCK_PARAM + BLOCKNO_PARAM + blockNumber;
final byte[] response = getRequest(urlParam);
if (response.length == 0) {
return Optional.empty();
}

try {
final UncleBlockResponseTO responseTO = convert(response, UncleBlockResponseTO.class);
final UncleBlockResponseTO responseTO = getRequest(urlParam, UncleBlockResponseTO.class);
if (responseTO.getMessage().startsWith("NOTOK")) {
return Optional.empty();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ final class ContractAPIProvider extends BasicProvider implements ContractAPI {
ContractAPIProvider(RequestQueueManager requestQueueManager,
String baseUrl,
EthHttpClient executor,
Converter converter) {
super(requestQueueManager, "contract", baseUrl, executor, converter);
Converter converter,
int retryCount) {
super(requestQueueManager, "contract", baseUrl, executor, converter, retryCount);
}

@NotNull
Expand Down
13 changes: 12 additions & 1 deletion src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ final class EthScanAPIBuilder implements EtherScanAPI.Builder {

private final Gson gson = new GsonConfiguration().builder().create();

private int retryCountOnLimitReach = 0;
private String apiKey = DEFAULT_KEY;
private RequestQueueManager queueManager;
private EthNetwork ethNetwork = EthNetworks.MAINNET;
Expand Down Expand Up @@ -87,6 +88,16 @@ public EtherScanAPI.Builder withConverter(@NotNull Supplier<Converter> converter
return this;
}

@NotNull
public EtherScanAPI.Builder withRetryOnLimitReach(int maxRetryCount) {
if (maxRetryCount < 0 || maxRetryCount > 20) {
throw new IllegalStateException("maxRetryCount value must be in range from 0 to 20, but was: " + maxRetryCount);
}

this.retryCountOnLimitReach = maxRetryCount;
return this;
}

@Override
public @NotNull EtherScanAPI build() {
RequestQueueManager requestQueueManager;
Expand All @@ -99,6 +110,6 @@ public EtherScanAPI.Builder withConverter(@NotNull Supplier<Converter> converter
}

return new EtherScanAPIProvider(apiKey, ethNetwork, requestQueueManager, ethHttpClientSupplier.get(),
converterSupplier.get());
converterSupplier.get(), retryCountOnLimitReach);
}
}
11 changes: 11 additions & 0 deletions src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package io.goodforgod.api.etherscan;

import io.goodforgod.api.etherscan.error.EtherScanRateLimitException;
import io.goodforgod.api.etherscan.http.EthHttpClient;
import io.goodforgod.api.etherscan.manager.RequestQueueManager;
import java.util.function.Supplier;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Range;

/**
* EtherScan full API Description <a href="https://etherscan.io/apis">...</a>
Expand Down Expand Up @@ -62,6 +64,15 @@ interface Builder {
@NotNull
Builder withConverter(@NotNull Supplier<Converter> converterSupplier);

/**
* By default is disabled
*
* @param maxRetryCount to retry if {@link EtherScanRateLimitException} thrown
* @return self
*/
@NotNull
EtherScanAPI.Builder withRetryOnLimitReach(@Range(from = 0, to = 20) int maxRetryCount);

@NotNull
EtherScanAPI build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,20 @@ final class EtherScanAPIProvider implements EtherScanAPI {
EthNetwork network,
RequestQueueManager queue,
EthHttpClient ethHttpClient,
Converter converter) {
Converter converter,
int retryCount) {
// EtherScan 1request\5sec limit support by queue manager
final String baseUrl = network.domain() + "?apikey=" + apiKey;

this.requestQueueManager = queue;
this.account = new AccountAPIProvider(queue, baseUrl, ethHttpClient, converter);
this.block = new BlockAPIProvider(queue, baseUrl, ethHttpClient, converter);
this.contract = new ContractAPIProvider(queue, baseUrl, ethHttpClient, converter);
this.logs = new LogsAPIProvider(queue, baseUrl, ethHttpClient, converter);
this.proxy = new ProxyAPIProvider(queue, baseUrl, ethHttpClient, converter);
this.stats = new StatisticAPIProvider(queue, baseUrl, ethHttpClient, converter);
this.txs = new TransactionAPIProvider(queue, baseUrl, ethHttpClient, converter);
this.gasTracker = new GasTrackerAPIProvider(queue, baseUrl, ethHttpClient, converter);
this.account = new AccountAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
this.block = new BlockAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
this.contract = new ContractAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
this.logs = new LogsAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
this.proxy = new ProxyAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
this.stats = new StatisticAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
this.txs = new TransactionAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
this.gasTracker = new GasTrackerAPIProvider(queue, baseUrl, ethHttpClient, converter, retryCount);
}

@NotNull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ final class GasTrackerAPIProvider extends BasicProvider implements GasTrackerAPI
GasTrackerAPIProvider(RequestQueueManager queue,
String baseUrl,
EthHttpClient ethHttpClient,
Converter converter) {
super(queue, "gastracker", baseUrl, ethHttpClient, converter);
Converter converter,
int retryCount) {
super(queue, "gastracker", baseUrl, ethHttpClient, converter, retryCount);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ final class LogsAPIProvider extends BasicProvider implements LogsAPI {
LogsAPIProvider(RequestQueueManager queue,
String baseUrl,
EthHttpClient executor,
Converter converter) {
super(queue, "logs", baseUrl, executor, converter);
Converter converter,
int retryCount) {
super(queue, "logs", baseUrl, executor, converter, retryCount);
}

@NotNull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,9 @@ final class ProxyAPIProvider extends BasicProvider implements ProxyAPI {
ProxyAPIProvider(RequestQueueManager queue,
String baseUrl,
EthHttpClient executor,
Converter converter) {
super(queue, "proxy", baseUrl, executor, converter);
Converter converter,
int retryCount) {
super(queue, "proxy", baseUrl, executor, converter, retryCount);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ final class StatisticAPIProvider extends BasicProvider implements StatisticAPI {
StatisticAPIProvider(RequestQueueManager queue,
String baseUrl,
EthHttpClient executor,
Converter converter) {
super(queue, "stats", baseUrl, executor, converter);
Converter converter,
int retry) {
super(queue, "stats", baseUrl, executor, converter, retry);
}

@NotNull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ final class TransactionAPIProvider extends BasicProvider implements TransactionA
TransactionAPIProvider(RequestQueueManager queue,
String baseUrl,
EthHttpClient executor,
Converter converter) {
super(queue, "transaction", baseUrl, executor, converter);
Converter converter,
int retryCount) {
super(queue, "transaction", baseUrl, executor, converter, retryCount);
}

@NotNull
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,27 @@ public interface RequestQueueManager extends AutoCloseable {
* Is used by default when no API KEY is provided
*/
static RequestQueueManager anonymous() {
return new SemaphoreRequestQueueManager(1, Duration.ofMillis(5015L));
return new SemaphoreRequestQueueManager(1, Duration.ofMillis(5045L));
}

/**
* Is available for all registered free API KEYs
* <a href="https://docs.etherscan.io/getting-started/viewing-api-usage-statistics">Free API KEY</a>
*/
static RequestQueueManager planFree() {
return new SemaphoreRequestQueueManager(5, Duration.ofMillis(1015L));
return new SemaphoreRequestQueueManager(5, Duration.ofMillis(1045L));
}

static RequestQueueManager planStandard() {
return new SemaphoreRequestQueueManager(10, Duration.ofMillis(1015L));
return new SemaphoreRequestQueueManager(10, Duration.ofMillis(1045L));
}

static RequestQueueManager planAdvanced() {
return new SemaphoreRequestQueueManager(20, Duration.ofMillis(1015L));
return new SemaphoreRequestQueueManager(20, Duration.ofMillis(1045L));
}

static RequestQueueManager planProfessional() {
return new SemaphoreRequestQueueManager(30, Duration.ofMillis(1015L));
return new SemaphoreRequestQueueManager(30, Duration.ofMillis(1045L));
}

static RequestQueueManager unlimited() {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/io/goodforgod/api/etherscan/model/Abi.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public int hashCode() {
@Override
public String toString() {
return "Abi{" +
"contractAbi='" + contractAbi + '\'' +
"contractAbi=" + contractAbi +
", isVerified=" + isVerified +
'}';
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public int hashCode() {
@Override
public String toString() {
return "Balance{" +
"address='" + address + '\'' +
"address=" + address +
", balance=" + balance +
'}';
}
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/io/goodforgod/api/etherscan/model/Block.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public String toString() {
return "Block{" +
"blockNumber=" + blockNumber +
", blockReward=" + blockReward +
", timeStamp='" + timeStamp + '\'' +
", timeStamp=" + timeStamp +
'}';
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public static class Uncle {
private BigInteger blockreward;
private int unclePosition;

private Uncle() {}
protected Uncle() {}

// <editor-fold desc="Getters">
public String getMiner() {
Expand Down Expand Up @@ -54,7 +54,7 @@ public int hashCode() {
@Override
public String toString() {
return "Uncle{" +
"miner='" + miner + '\'' +
"miner=" + miner +
", blockreward=" + blockreward +
", unclePosition=" + unclePosition +
'}';
Expand Down Expand Up @@ -128,9 +128,9 @@ public String getUncleInclusionReward() {
@Override
public String toString() {
return "UncleBlock{" +
"blockMiner='" + blockMiner + '\'' +
"blockMiner=" + blockMiner +
", uncles=" + uncles +
", uncleInclusionReward='" + uncleInclusionReward + '\'' +
", uncleInclusionReward=" + uncleInclusionReward +
'}';
}

Expand Down
Loading

0 comments on commit 3a87ca8

Please sign in to comment.