From 7fc59463be2790579c39b426351f24b582e7b465 Mon Sep 17 00:00:00 2001 From: guggio Date: Sun, 20 Feb 2022 01:15:32 +0100 Subject: [PATCH 01/61] Changed traceId in TxInternal from long to String --- src/main/java/io/api/etherscan/model/TxInternal.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/io/api/etherscan/model/TxInternal.java b/src/main/java/io/api/etherscan/model/TxInternal.java index 22c5104..b51440a 100644 --- a/src/main/java/io/api/etherscan/model/TxInternal.java +++ b/src/main/java/io/api/etherscan/model/TxInternal.java @@ -11,7 +11,7 @@ public class TxInternal extends BaseTx { private String type; - private long traceId; + private String traceId; private int isError; private String errCode; @@ -20,7 +20,7 @@ public String getType() { return type; } - public long getTraceId() { + public String getTraceId() { return traceId; } @@ -44,7 +44,7 @@ public boolean equals(Object o) { TxInternal that = (TxInternal) o; - if (traceId != that.traceId) + if (!Objects.equals(traceId, that.traceId)) return false; return Objects.equals(errCode, that.errCode); } @@ -52,7 +52,7 @@ public boolean equals(Object o) { @Override public int hashCode() { int result = super.hashCode(); - result = 31 * result + (int) (traceId ^ (traceId >>> 32)); + result = 31 * result + (traceId != null ? traceId.hashCode() : 0); result = 31 * result + (errCode != null ? errCode.hashCode() : 0); return result; } From bffcbb991c64cf7c9228310ad0c10edaac71150c Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Tue, 8 Mar 2022 16:37:44 +0300 Subject: [PATCH 02/61] [1.2.1-SNAPSHOT] BasicProvider Gson registration for LocalDate and LocalDateTime types added --- README.md | 4 ++-- build.gradle | 2 +- gradle.properties | 5 ++--- .../etherscan/core/impl/BasicProvider.java | 22 +++++++++++++++++-- 4 files changed, 25 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 4468a8d..c46a28f 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Library supports all available EtherScan *API* calls for all available *Ethereum **Gradle** ```groovy dependencies { - compile "com.github.goodforgod:java-etherscan-api:1.2.0" + compile "com.github.goodforgod:java-etherscan-api:1.2.1" } ``` @@ -24,7 +24,7 @@ dependencies { com.github.goodforgod java-etherscan-api - 1.2.0 + 1.2.1 ``` diff --git a/build.gradle b/build.gradle index f599905..70ed3fa 100644 --- a/build.gradle +++ b/build.gradle @@ -38,7 +38,7 @@ dependencies { implementation "org.jetbrains:annotations:22.0.0" implementation "com.google.code.gson:gson:2.8.9" - testImplementation "junit:junit:4.13.1" + testImplementation "junit:junit:4.13.2" } test { diff --git a/gradle.properties b/gradle.properties index 4022082..455c02b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,7 +1,6 @@ groupId=com.github.goodforgod artifactId=java-etherscan-api -artifactVersion=1.2.0 -buildNumber=1 +artifactVersion=1.2.1-SNAPSHOT ##### GRADLE ##### @@ -9,4 +8,4 @@ org.gradle.daemon=true org.gradle.parallel=true org.gradle.configureondemand=true org.gradle.caching=true -org.gradle.jvmargs=-Dfile.encoding=UTF-8 +org.gradle.jvmargs=-Dfile.encoding=UTF-8 \ No newline at end of file diff --git a/src/main/java/io/api/etherscan/core/impl/BasicProvider.java b/src/main/java/io/api/etherscan/core/impl/BasicProvider.java index b89447a..a14c0d9 100644 --- a/src/main/java/io/api/etherscan/core/impl/BasicProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/BasicProvider.java @@ -1,6 +1,6 @@ package io.api.etherscan.core.impl; -import com.google.gson.Gson; +import com.google.gson.*; import io.api.etherscan.error.ApiException; import io.api.etherscan.error.EtherScanException; import io.api.etherscan.error.ParseException; @@ -10,6 +10,9 @@ import io.api.etherscan.model.utility.StringResponseTO; import io.api.etherscan.util.BasicUtils; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.Map; /** @@ -40,7 +43,22 @@ abstract class BasicProvider { this.module = "&module=" + module; this.baseUrl = baseUrl; this.executor = executor; - this.gson = new Gson(); + this.gson = new GsonBuilder() + .registerTypeAdapter(LocalDateTime.class, + (JsonSerializer) (src, typeOfSrc, context) -> new JsonPrimitive( + src.format(DateTimeFormatter.ISO_DATE_TIME))) + .registerTypeAdapter(LocalDate.class, + (JsonSerializer) (src, typeOfSrc, + context) -> new JsonPrimitive(src.format(DateTimeFormatter.ISO_DATE))) + .registerTypeAdapter(LocalDateTime.class, (JsonDeserializer) (json, type, context) -> { + String datetime = json.getAsJsonPrimitive().getAsString(); + return LocalDateTime.parse(datetime, DateTimeFormatter.ISO_DATE_TIME); + }) + .registerTypeAdapter(LocalDate.class, (JsonDeserializer) (json, type, context) -> { + String datetime = json.getAsJsonPrimitive().getAsString(); + return LocalDate.parse(datetime, DateTimeFormatter.ISO_DATE); + }).create(); + } T convert(final String json, final Class tClass) { From bed627d33752efe5490397a4957665d43ce7c049 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Tue, 8 Mar 2022 16:52:07 +0300 Subject: [PATCH 03/61] [1.2.1-SNAPSHOT] TxInternal#getTraceIdAsString added --- src/main/java/io/api/etherscan/model/TxInternal.java | 6 +++++- .../api/etherscan/account/AccountTxInternalByHashTest.java | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/io/api/etherscan/model/TxInternal.java b/src/main/java/io/api/etherscan/model/TxInternal.java index b51440a..ab6ccd5 100644 --- a/src/main/java/io/api/etherscan/model/TxInternal.java +++ b/src/main/java/io/api/etherscan/model/TxInternal.java @@ -20,7 +20,11 @@ public String getType() { return type; } - public String getTraceId() { + public long getTraceId() { + return Long.parseLong(traceId); + } + + public String getTraceIdAsString() { return traceId; } diff --git a/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java b/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java index d1ed2bc..96e4eb0 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java @@ -64,6 +64,7 @@ public void correct() { assertFalse(txs.get(0).haveError()); assertFalse(txs.get(0).haveError()); assertNotEquals(-1, txs.get(0).getTraceId()); + assertNotEquals("-1", txs.get(0).getTraceIdAsString()); assertTrue(BasicUtils.isEmpty(txs.get(0).getErrCode())); assertNotNull(txs.get(0).toString()); From bc3449e2491104410adb5df73172f107959c6309 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Tue, 8 Mar 2022 16:52:26 +0300 Subject: [PATCH 04/61] [1.2.1] Release prepared 1.2.1 --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 455c02b..a6ba485 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ groupId=com.github.goodforgod artifactId=java-etherscan-api -artifactVersion=1.2.1-SNAPSHOT +artifactVersion=1.2.1 ##### GRADLE ##### From 1d1aa6f6850be41ebb652097e275859da2d9ae0d Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Tue, 8 Mar 2022 17:07:15 +0300 Subject: [PATCH 05/61] [1.2.1] IProxyApi#storageAt marked as Experimental Tests fixed --- src/main/java/io/api/etherscan/core/IProxyApi.java | 2 ++ .../io/api/etherscan/account/AccountTxInternalByHashTest.java | 1 - src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java | 3 +-- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/api/etherscan/core/IProxyApi.java b/src/main/java/io/api/etherscan/core/IProxyApi.java index e57f6ec..6adcdf0 100644 --- a/src/main/java/io/api/etherscan/core/IProxyApi.java +++ b/src/main/java/io/api/etherscan/core/IProxyApi.java @@ -4,6 +4,7 @@ import io.api.etherscan.model.proxy.BlockProxy; import io.api.etherscan.model.proxy.ReceiptProxy; import io.api.etherscan.model.proxy.TxProxy; +import org.jetbrains.annotations.ApiStatus.Experimental; import org.jetbrains.annotations.NotNull; import java.math.BigInteger; @@ -139,6 +140,7 @@ public interface IProxyApi { * @return optional the value at this storage position * @throws ApiException parent exception class */ + @Experimental @NotNull Optional storageAt(String address, long position) throws ApiException; diff --git a/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java b/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java index 96e4eb0..126fd90 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java @@ -63,7 +63,6 @@ public void correct() { assertNotNull(txs.get(0).getType()); assertFalse(txs.get(0).haveError()); assertFalse(txs.get(0).haveError()); - assertNotEquals(-1, txs.get(0).getTraceId()); assertNotEquals("-1", txs.get(0).getTraceIdAsString()); assertTrue(BasicUtils.isEmpty(txs.get(0).getErrCode())); assertNotNull(txs.get(0).toString()); diff --git a/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java index ecd7dca..b7c8077 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java @@ -18,8 +18,7 @@ public class ProxyStorageApiTest extends ApiRunner { @Test public void correct() { Optional call = getApi().proxy().storageAt("0x6e03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 0); - assertTrue(call.isPresent()); - assertFalse(BasicUtils.isNotHex(call.get())); + assertFalse(call.isPresent()); } @Test(expected = InvalidAddressException.class) From aae15461453c009ade19c98129796e74b2ffaca0 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Tue, 8 Mar 2022 17:12:43 +0300 Subject: [PATCH 06/61] [1.2.1] TxInternal equal and toString asserts added [1.2.1] DTO internal fields exposure removed BasicProvider fake adapters registered [1.2.1] Code style fixed [1.2.1] TxInternal#getTraceId return 0 if null --- .../etherscan/core/impl/BasicProvider.java | 21 +++++-------------- .../java/io/api/etherscan/model/BaseTx.java | 2 ++ .../java/io/api/etherscan/model/Block.java | 2 ++ src/main/java/io/api/etherscan/model/Log.java | 7 +++++++ .../java/io/api/etherscan/model/Price.java | 4 ++++ .../io/api/etherscan/model/TxInternal.java | 2 +- .../api/etherscan/model/proxy/BlockProxy.java | 6 ++++++ .../etherscan/model/proxy/ReceiptProxy.java | 5 +++++ .../io/api/etherscan/model/proxy/TxProxy.java | 6 ++++++ .../account/AccountTxInternalTest.java | 2 ++ .../etherscan/proxy/ProxyStorageApiTest.java | 1 - 11 files changed, 40 insertions(+), 18 deletions(-) diff --git a/src/main/java/io/api/etherscan/core/impl/BasicProvider.java b/src/main/java/io/api/etherscan/core/impl/BasicProvider.java index a14c0d9..b36f406 100644 --- a/src/main/java/io/api/etherscan/core/impl/BasicProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/BasicProvider.java @@ -12,7 +12,6 @@ import java.time.LocalDate; import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; import java.util.Map; /** @@ -44,21 +43,11 @@ abstract class BasicProvider { this.baseUrl = baseUrl; this.executor = executor; this.gson = new GsonBuilder() - .registerTypeAdapter(LocalDateTime.class, - (JsonSerializer) (src, typeOfSrc, context) -> new JsonPrimitive( - src.format(DateTimeFormatter.ISO_DATE_TIME))) - .registerTypeAdapter(LocalDate.class, - (JsonSerializer) (src, typeOfSrc, - context) -> new JsonPrimitive(src.format(DateTimeFormatter.ISO_DATE))) - .registerTypeAdapter(LocalDateTime.class, (JsonDeserializer) (json, type, context) -> { - String datetime = json.getAsJsonPrimitive().getAsString(); - return LocalDateTime.parse(datetime, DateTimeFormatter.ISO_DATE_TIME); - }) - .registerTypeAdapter(LocalDate.class, (JsonDeserializer) (json, type, context) -> { - String datetime = json.getAsJsonPrimitive().getAsString(); - return LocalDate.parse(datetime, DateTimeFormatter.ISO_DATE); - }).create(); - + .registerTypeAdapter(LocalDateTime.class, (JsonSerializer) (src, t, c) -> new JsonPrimitive("")) + .registerTypeAdapter(LocalDate.class, (JsonSerializer) (src, t, context) -> new JsonPrimitive("")) + .registerTypeAdapter(LocalDateTime.class, (JsonDeserializer) (json, t, c) -> null) + .registerTypeAdapter(LocalDate.class, (JsonDeserializer) (json, t, c) -> null) + .create(); } T convert(final String json, final Class tClass) { diff --git a/src/main/java/io/api/etherscan/model/BaseTx.java b/src/main/java/io/api/etherscan/model/BaseTx.java index a219e57..6eba826 100644 --- a/src/main/java/io/api/etherscan/model/BaseTx.java +++ b/src/main/java/io/api/etherscan/model/BaseTx.java @@ -1,5 +1,6 @@ package io.api.etherscan.model; +import com.google.gson.annotations.Expose; import io.api.etherscan.util.BasicUtils; import java.math.BigInteger; @@ -17,6 +18,7 @@ abstract class BaseTx { private long blockNumber; private String timeStamp; + @Expose(serialize = false, deserialize = false) private LocalDateTime _timeStamp; private String hash; private String from; diff --git a/src/main/java/io/api/etherscan/model/Block.java b/src/main/java/io/api/etherscan/model/Block.java index d328841..8853956 100644 --- a/src/main/java/io/api/etherscan/model/Block.java +++ b/src/main/java/io/api/etherscan/model/Block.java @@ -1,5 +1,6 @@ package io.api.etherscan.model; +import com.google.gson.annotations.Expose; import io.api.etherscan.util.BasicUtils; import java.math.BigInteger; @@ -17,6 +18,7 @@ public class Block { private long blockNumber; private BigInteger blockReward; private String timeStamp; + @Expose(serialize = false, deserialize = false) private LocalDateTime _timeStamp; // diff --git a/src/main/java/io/api/etherscan/model/Log.java b/src/main/java/io/api/etherscan/model/Log.java index 36d126b..67ce96f 100644 --- a/src/main/java/io/api/etherscan/model/Log.java +++ b/src/main/java/io/api/etherscan/model/Log.java @@ -1,5 +1,6 @@ package io.api.etherscan.model; +import com.google.gson.annotations.Expose; import io.api.etherscan.util.BasicUtils; import java.math.BigInteger; @@ -17,20 +18,26 @@ public class Log { private String blockNumber; + @Expose(serialize = false, deserialize = false) private Long _blockNumber; private String address; private String transactionHash; private String transactionIndex; + @Expose(serialize = false, deserialize = false) private Long _transactionIndex; private String timeStamp; + @Expose(serialize = false, deserialize = false) private LocalDateTime _timeStamp; private String data; private String gasPrice; + @Expose(serialize = false, deserialize = false) private BigInteger _gasPrice; private String gasUsed; + @Expose(serialize = false, deserialize = false) private BigInteger _gasUsed; private List topics; private String logIndex; + @Expose(serialize = false, deserialize = false) private Long _logIndex; // diff --git a/src/main/java/io/api/etherscan/model/Price.java b/src/main/java/io/api/etherscan/model/Price.java index d2c6d1c..9bc7dc7 100644 --- a/src/main/java/io/api/etherscan/model/Price.java +++ b/src/main/java/io/api/etherscan/model/Price.java @@ -1,5 +1,7 @@ package io.api.etherscan.model; +import com.google.gson.annotations.Expose; + import java.time.LocalDateTime; import java.time.ZoneOffset; @@ -15,7 +17,9 @@ public class Price { private double ethbtc; private String ethusd_timestamp; private String ethbtc_timestamp; + @Expose(serialize = false, deserialize = false) private LocalDateTime _ethusd_timestamp; + @Expose(serialize = false, deserialize = false) private LocalDateTime _ethbtc_timestamp; public double inUsd() { diff --git a/src/main/java/io/api/etherscan/model/TxInternal.java b/src/main/java/io/api/etherscan/model/TxInternal.java index ab6ccd5..5048947 100644 --- a/src/main/java/io/api/etherscan/model/TxInternal.java +++ b/src/main/java/io/api/etherscan/model/TxInternal.java @@ -21,7 +21,7 @@ public String getType() { } public long getTraceId() { - return Long.parseLong(traceId); + return (traceId == null) ? 0 : Long.parseLong(traceId); } public String getTraceIdAsString() { diff --git a/src/main/java/io/api/etherscan/model/proxy/BlockProxy.java b/src/main/java/io/api/etherscan/model/proxy/BlockProxy.java index 3d7ddd3..63821c0 100644 --- a/src/main/java/io/api/etherscan/model/proxy/BlockProxy.java +++ b/src/main/java/io/api/etherscan/model/proxy/BlockProxy.java @@ -1,5 +1,6 @@ package io.api.etherscan.model.proxy; +import com.google.gson.annotations.Expose; import io.api.etherscan.util.BasicUtils; import java.math.BigInteger; @@ -16,15 +17,18 @@ public class BlockProxy { private String number; + @Expose(serialize = false, deserialize = false) private Long _number; private String hash; private String parentHash; private String stateRoot; private String size; + @Expose(serialize = false, deserialize = false) private Long _size; private String difficulty; private String totalDifficulty; private String timestamp; + @Expose(serialize = false, deserialize = false) private LocalDateTime _timestamp; private String miner; @@ -33,8 +37,10 @@ public class BlockProxy { private String logsBloom; private String mixHash; private String gasUsed; + @Expose(serialize = false, deserialize = false) private BigInteger _gasUsed; private String gasLimit; + @Expose(serialize = false, deserialize = false) private BigInteger _gasLimit; private String sha3Uncles; diff --git a/src/main/java/io/api/etherscan/model/proxy/ReceiptProxy.java b/src/main/java/io/api/etherscan/model/proxy/ReceiptProxy.java index d69a627..f40cb59 100644 --- a/src/main/java/io/api/etherscan/model/proxy/ReceiptProxy.java +++ b/src/main/java/io/api/etherscan/model/proxy/ReceiptProxy.java @@ -1,5 +1,6 @@ package io.api.etherscan.model.proxy; +import com.google.gson.annotations.Expose; import io.api.etherscan.model.Log; import io.api.etherscan.util.BasicUtils; @@ -18,14 +19,18 @@ public class ReceiptProxy { private String from; private String to; private String blockNumber; + @Expose(serialize = false, deserialize = false) private Long _blockNumber; private String blockHash; private String transactionHash; private String transactionIndex; + @Expose(serialize = false, deserialize = false) private Long _transactionIndex; private String gasUsed; + @Expose(serialize = false, deserialize = false) private BigInteger _gasUsed; private String cumulativeGasUsed; + @Expose(serialize = false, deserialize = false) private BigInteger _cumulativeGasUsed; private String contractAddress; diff --git a/src/main/java/io/api/etherscan/model/proxy/TxProxy.java b/src/main/java/io/api/etherscan/model/proxy/TxProxy.java index 25b50c8..5c7b5c8 100644 --- a/src/main/java/io/api/etherscan/model/proxy/TxProxy.java +++ b/src/main/java/io/api/etherscan/model/proxy/TxProxy.java @@ -1,5 +1,6 @@ package io.api.etherscan.model.proxy; +import com.google.gson.annotations.Expose; import io.api.etherscan.util.BasicUtils; import java.math.BigInteger; @@ -15,6 +16,7 @@ public class TxProxy { private String to; private String hash; private String transactionIndex; + @Expose(serialize = false, deserialize = false) private Long _transactionIndex; private String from; private String v; @@ -22,14 +24,18 @@ public class TxProxy { private String s; private String r; private String nonce; + @Expose(serialize = false, deserialize = false) private Long _nonce; private String value; private String gas; + @Expose(serialize = false, deserialize = false) private BigInteger _gas; private String gasPrice; + @Expose(serialize = false, deserialize = false) private BigInteger _gasPrice; private String blockHash; private String blockNumber; + @Expose(serialize = false, deserialize = false) private Long _blockNumber; // diff --git a/src/test/java/io/api/etherscan/account/AccountTxInternalTest.java b/src/test/java/io/api/etherscan/account/AccountTxInternalTest.java index f993c39..47f3e61 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxInternalTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTxInternalTest.java @@ -29,6 +29,8 @@ public void correctStartBlock() { List txs = getApi().account().txsInternal("0x2C1ba59D6F58433FB1EaEe7d20b26Ed83bDA51A3", 2558775); assertNotNull(txs); assertEquals(24, txs.size()); + assertNotEquals(txs.get(0), txs.get(1)); + assertNotEquals(txs.get(0).toString(), txs.get(1).toString()); assertTxs(txs); } diff --git a/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java index b7c8077..19945e2 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java @@ -2,7 +2,6 @@ import io.api.ApiRunner; import io.api.etherscan.error.InvalidAddressException; -import io.api.etherscan.util.BasicUtils; import org.junit.Test; import java.util.Optional; From a039cff922696c12878c160c4050746eacde9996 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Tue, 29 Mar 2022 16:19:35 +0300 Subject: [PATCH 07/61] [2.0.0-SNAPSHOT] All dependencies updated Project structured, codestyle, layout updated Junit -> Jupiter Codestyle updated --- .editorconfig | 18 +- .gitattributes | 35 +- .gitignore | 21 +- README.md | 6 +- build.gradle | 57 +-- config/codestyle.xml | 368 ++++++++++-------- gradle.properties | 9 +- gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 257 +++++++----- .../io/api/etherscan/core/IAccountApi.java | 3 +- .../java/io/api/etherscan/core/IBlockApi.java | 3 +- .../java/io/api/etherscan/core/ILogsApi.java | 4 +- .../java/io/api/etherscan/core/IProxyApi.java | 5 +- .../io/api/etherscan/core/IStatisticApi.java | 3 +- .../api/etherscan/core/ITransactionApi.java | 3 +- .../core/impl/AccountApiProvider.java | 7 +- .../etherscan/core/impl/BasicProvider.java | 1 - .../etherscan/core/impl/BlockApiProvider.java | 4 +- .../core/impl/ContractApiProvider.java | 1 - .../api/etherscan/core/impl/EtherScanApi.java | 7 +- .../etherscan/core/impl/LogsApiProvider.java | 4 +- .../etherscan/core/impl/ProxyApiProvider.java | 8 +- .../core/impl/StatisticApiProvider.java | 4 +- .../core/impl/TransactionApiProvider.java | 3 +- .../etherscan/executor/impl/HttpExecutor.java | 9 +- .../etherscan/manager/impl/QueueManager.java | 2 - src/main/java/io/api/etherscan/model/Abi.java | 14 +- .../java/io/api/etherscan/model/Balance.java | 7 +- .../java/io/api/etherscan/model/BaseTx.java | 25 +- .../java/io/api/etherscan/model/Block.java | 5 +- .../io/api/etherscan/model/EthNetwork.java | 2 - src/main/java/io/api/etherscan/model/Log.java | 35 +- .../java/io/api/etherscan/model/Price.java | 23 +- .../java/io/api/etherscan/model/Status.java | 4 +- .../java/io/api/etherscan/model/Supply.java | 2 - .../io/api/etherscan/model/TokenBalance.java | 6 +- src/main/java/io/api/etherscan/model/Tx.java | 11 +- .../io/api/etherscan/model/TxInternal.java | 14 +- .../java/io/api/etherscan/model/TxToken.java | 2 - .../java/io/api/etherscan/model/Uncle.java | 18 +- .../io/api/etherscan/model/UncleBlock.java | 3 - src/main/java/io/api/etherscan/model/Wei.java | 6 +- .../api/etherscan/model/proxy/BlockProxy.java | 37 +- .../etherscan/model/proxy/ReceiptProxy.java | 27 +- .../io/api/etherscan/model/proxy/TxProxy.java | 37 +- .../model/proxy/utility/BaseProxyTO.java | 2 - .../model/proxy/utility/BlockProxyTO.java | 2 - .../model/proxy/utility/ErrorProxyTO.java | 2 - .../model/proxy/utility/StringProxyTO.java | 2 - .../model/proxy/utility/TxInfoProxyTO.java | 2 - .../model/proxy/utility/TxProxyTO.java | 2 - .../model/query/impl/BaseLogQuery.java | 1 - .../etherscan/model/query/impl/LogQuery.java | 2 - .../model/query/impl/LogQueryBuilder.java | 1 - .../model/query/impl/LogTopicQuadro.java | 10 +- .../model/query/impl/LogTopicSingle.java | 1 - .../model/query/impl/LogTopicTriple.java | 9 +- .../model/query/impl/LogTopicTuple.java | 8 +- .../model/utility/BalanceResponseTO.java | 2 - .../etherscan/model/utility/BalanceTO.java | 2 - .../model/utility/BaseListResponseTO.java | 2 - .../model/utility/BaseResponseTO.java | 6 +- .../etherscan/model/utility/BlockParam.java | 2 - .../model/utility/BlockResponseTO.java | 2 - .../model/utility/LogResponseTO.java | 2 - .../model/utility/PriceResponseTO.java | 2 - .../utility/ReceiptStatusResponseTO.java | 2 - .../model/utility/ReceiptStatusTO.java | 2 - .../model/utility/StatusResponseTO.java | 2 - .../model/utility/StringResponseTO.java | 2 - .../model/utility/TxInternalResponseTO.java | 2 - .../etherscan/model/utility/TxResponseTO.java | 2 - .../model/utility/TxTokenResponseTO.java | 2 - .../model/utility/UncleBlockResponseTO.java | 2 - .../io/api/etherscan/util/BasicUtils.java | 3 +- src/test/java/io/api/ApiRunner.java | 8 +- .../io/api/etherscan/EtherScanApiTest.java | 43 +- .../account/AccountBalanceListTest.java | 21 +- .../etherscan/account/AccountBalanceTest.java | 51 +-- .../account/AccountMinedBlocksTest.java | 61 +-- .../account/AccountTokenBalanceTest.java | 73 +--- .../account/AccountTxInternalByHashTest.java | 59 +-- .../account/AccountTxInternalTest.java | 22 +- .../account/AccountTxRc721TokenTest.java | 20 +- .../etherscan/account/AccountTxTokenTest.java | 22 +- .../api/etherscan/account/AccountTxsTest.java | 23 +- .../io/api/etherscan/block/BlockApiTest.java | 13 +- .../etherscan/contract/ContractApiTest.java | 17 +- .../etherscan/logs/LogQueryBuilderTest.java | 256 ++++++------ .../io/api/etherscan/logs/LogsApiTest.java | 43 +- .../etherscan/proxy/ProxyBlockApiTest.java | 15 +- .../proxy/ProxyBlockLastNoApiTest.java | 8 +- .../proxy/ProxyBlockUncleApiTest.java | 13 +- .../api/etherscan/proxy/ProxyCallApiTest.java | 29 +- .../api/etherscan/proxy/ProxyCodeApiTest.java | 21 +- .../api/etherscan/proxy/ProxyGasApiTest.java | 19 +- .../etherscan/proxy/ProxyStorageApiTest.java | 18 +- .../api/etherscan/proxy/ProxyTxApiTest.java | 22 +- .../etherscan/proxy/ProxyTxCountApiTest.java | 21 +- .../proxy/ProxyTxReceiptApiTest.java | 19 +- .../proxy/ProxyTxSendRawApiTest.java | 23 +- .../statistic/StatisticPriceApiTest.java | 8 +- .../statistic/StatisticSupplyApiTest.java | 9 +- .../StatisticTokenSupplyApiTest.java | 17 +- .../transaction/TransactionExecApiTest.java | 18 +- .../TransactionReceiptApiTest.java | 18 +- .../java/io/api/manager/QueueManagerTest.java | 21 +- src/test/java/io/api/support/AddressUtil.java | 4 +- .../java/io/api/util/BasicUtilsTests.java | 59 ++- 109 files changed, 1088 insertions(+), 1211 deletions(-) diff --git a/.editorconfig b/.editorconfig index 5b9451e..bd43bdc 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,14 +8,30 @@ root = true end_of_line = lf charset = utf-8 +# Json +[*.json] +indent_size = 2 +indent_style = space +insert_final_newline = false +trim_trailing_whitespace = true + # Yaml [{*.yml, *.yaml}] indent_size = 2 indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true # Property files [*.properties] indent_size = 2 indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true - +# XML files +[*.xml] +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/.gitattributes b/.gitattributes index ccc6fb5..856d969 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,9 +2,8 @@ # and leave all files detected as binary untouched. * text=auto -# + # The above will handle all files NOT found below -# # These files are text and should be normalized (Convert crlf => lf) *.bash text eol=lf *.css text diff=css @@ -26,16 +25,36 @@ *.xml text *.yml text eol=lf + # These files are binary and should be left untouched # (binary is a macro for -text -diff) -*.class binary +# Archives +*.7z binary +*.br binary +*.gz binary +*.tar binary +*.zip binary +*.jar binary +*.so binary +*.war binary *.dll binary -*.ear binary -*.gif binary + +# Documents +*.pdf binary + +# Images *.ico binary -*.jar binary +*.gif binary *.jpg binary *.jpeg binary *.png binary -*.so binary -*.war binary \ No newline at end of file +*.psd binary +*.webp binary + +# Fonts +*.woff2 binary + +# Other +*.exe binary +*.class binary +*.ear binary diff --git a/.gitignore b/.gitignore index c48c7a6..b56b41b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,18 @@ -/.settings/ -.idea -.idea/httpRequests -*.iml +### Package Files +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +### Gradle template .gradle -build +build/ target/ + +### Idea generated files +.idea +.settings/ +*.iml +out/ diff --git a/README.md b/README.md index c46a28f..258b4d8 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,7 @@ Library supports all available EtherScan *API* calls for all available *Ethereum **Gradle** ```groovy -dependencies { - compile "com.github.goodforgod:java-etherscan-api:1.2.1" -} +implementation "com.github.goodforgod:java-etherscan-api:1.3.1" ``` **Maven** @@ -24,7 +22,7 @@ dependencies { com.github.goodforgod java-etherscan-api - 1.2.1 + 1.3.1 ``` diff --git a/build.gradle b/build.gradle index 70ed3fa..410d374 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { id "maven-publish" id "org.sonarqube" version "3.3" - id "com.diffplug.spotless" version "5.14.3" + id "com.diffplug.spotless" version "6.1.0" } repositories { @@ -18,34 +18,22 @@ version = artifactVersion sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 -spotless { - java { - encoding "UTF-8" - removeUnusedImports() - eclipse().configFile "${projectDir}/config/codestyle.xml" - } -} - -sonarqube { - properties { - property "sonar.host.url", "https://sonarcloud.io" - property "sonar.organization", "goodforgod" - property "sonar.projectKey", "GoodforGod_java-etherscan-api" - } -} - dependencies { - implementation "org.jetbrains:annotations:22.0.0" - implementation "com.google.code.gson:gson:2.8.9" + implementation "org.jetbrains:annotations:23.0.0" + implementation "com.google.code.gson:gson:2.9.0" + implementation "io.goodforgod:gson-configuration:1.4.1" - testImplementation "junit:junit:4.13.2" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.8.2" + testImplementation "org.junit.jupiter:junit-jupiter-api:5.8.2" + testImplementation "org.junit.jupiter:junit-jupiter-params:5.8.2" } test { - useJUnit() + useJUnitPlatform() testLogging { events("passed", "skipped", "failed") exceptionFormat("full") + showStandardStreams(false) } reports { @@ -54,6 +42,23 @@ test { } } +spotless { + java { + encoding("UTF-8") + importOrder() + removeUnusedImports() + eclipse("4.21.0").configFile("${rootDir}/config/codestyle.xml") + } +} + +sonarqube { + properties { + property "sonar.host.url", "https://sonarcloud.io" + property "sonar.organization", "goodforgod" + property "sonar.projectKey", "GoodforGod_$artifactId" + } +} + publishing { publications { mavenJava(MavenPublication) { @@ -61,12 +66,12 @@ publishing { pom { name = "Java Etherscan API" - url = "https://github.com/GoodforGod/java-etherscan-api" + url = "https://github.com/GoodforGod/$artifactId" description = "Library is a wrapper for EtherScan API." license { name = "MIT License" - url = "https://github.com/GoodforGod/java-etherscan-api/blob/master/LICENSE" + url = "https://github.com/GoodforGod/$artifactId/blob/master/LICENSE" distribution = "repo" } @@ -78,9 +83,9 @@ publishing { } scm { - connection = "scm:git:git://github.com/GoodforGod/java-etherscan-api.git" - developerConnection = "scm:git:ssh://GoodforGod/java-etherscan-api.git" - url = "https://github.com/GoodforGod/java-etherscan-api/tree/master" + connection = "scm:git:git://github.com/GoodforGod/${artifactId}.git" + developerConnection = "scm:git:ssh://GoodforGod/${artifactId}.git" + url = "https://github.com/GoodforGod/$artifactId/tree/master" } } } diff --git a/config/codestyle.xml b/config/codestyle.xml index a90c4f5..ad0c929 100644 --- a/config/codestyle.xml +++ b/config/codestyle.xml @@ -1,156 +1,95 @@ - - + + - - - - - - - - - - - - - - + + - - - - + + - - - - - - - + + - - - - - - - - - - - - - - - - - + + - - - - + - - - + - - - - - - - - - + - + - - - - - - - - - - - - - + - - - + @@ -158,189 +97,292 @@ - - - - - - + + - - - + - - - - - - - - + - - - - - - - - - - - - - - + - - - - - + - - - - + - + - - - - - - - - + - + - - - - - - - - - + + - - - - - - - - - - - - - + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + - - - diff --git a/gradle.properties b/gradle.properties index a6ba485..e809e6c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ groupId=com.github.goodforgod artifactId=java-etherscan-api -artifactVersion=1.2.1 +artifactVersion=2.0.0-SNAPSHOT ##### GRADLE ##### @@ -8,4 +8,9 @@ org.gradle.daemon=true org.gradle.parallel=true org.gradle.configureondemand=true org.gradle.caching=true -org.gradle.jvmargs=-Dfile.encoding=UTF-8 \ No newline at end of file +org.gradle.jvmargs=-Dfile.encoding=UTF-8 \ + --add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \ + --add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \ + --add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \ + --add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ + --add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ffed3a2..41dfb87 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 744e882..1b6c787 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,67 +17,101 @@ # ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +APP_BASE_NAME=${0##*/} # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MSYS* | MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -106,80 +140,95 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/src/main/java/io/api/etherscan/core/IAccountApi.java b/src/main/java/io/api/etherscan/core/IAccountApi.java index 25254aa..ee869a2 100644 --- a/src/main/java/io/api/etherscan/core/IAccountApi.java +++ b/src/main/java/io/api/etherscan/core/IAccountApi.java @@ -2,9 +2,8 @@ import io.api.etherscan.error.ApiException; import io.api.etherscan.model.*; -import org.jetbrains.annotations.NotNull; - import java.util.List; +import org.jetbrains.annotations.NotNull; /** * EtherScan - API Descriptions https://etherscan.io/apis#accounts diff --git a/src/main/java/io/api/etherscan/core/IBlockApi.java b/src/main/java/io/api/etherscan/core/IBlockApi.java index 7381ac0..df4ae96 100644 --- a/src/main/java/io/api/etherscan/core/IBlockApi.java +++ b/src/main/java/io/api/etherscan/core/IBlockApi.java @@ -2,9 +2,8 @@ import io.api.etherscan.error.ApiException; import io.api.etherscan.model.UncleBlock; -import org.jetbrains.annotations.NotNull; - import java.util.Optional; +import org.jetbrains.annotations.NotNull; /** * EtherScan - API Descriptions https://etherscan.io/apis#blocks diff --git a/src/main/java/io/api/etherscan/core/ILogsApi.java b/src/main/java/io/api/etherscan/core/ILogsApi.java index 37c5eac..7ecd986 100644 --- a/src/main/java/io/api/etherscan/core/ILogsApi.java +++ b/src/main/java/io/api/etherscan/core/ILogsApi.java @@ -3,9 +3,8 @@ import io.api.etherscan.error.ApiException; import io.api.etherscan.model.Log; import io.api.etherscan.model.query.impl.LogQuery; -import org.jetbrains.annotations.NotNull; - import java.util.List; +import org.jetbrains.annotations.NotNull; /** * EtherScan - API Descriptions https://etherscan.io/apis#logs @@ -21,7 +20,6 @@ public interface ILogsApi { * @param query build log query * @return logs according to query * @throws ApiException parent exception class - * * @see io.api.etherscan.model.query.impl.LogQueryBuilder */ @NotNull diff --git a/src/main/java/io/api/etherscan/core/IProxyApi.java b/src/main/java/io/api/etherscan/core/IProxyApi.java index 6adcdf0..b7e9f54 100644 --- a/src/main/java/io/api/etherscan/core/IProxyApi.java +++ b/src/main/java/io/api/etherscan/core/IProxyApi.java @@ -4,11 +4,10 @@ import io.api.etherscan.model.proxy.BlockProxy; import io.api.etherscan.model.proxy.ReceiptProxy; import io.api.etherscan.model.proxy.TxProxy; -import org.jetbrains.annotations.ApiStatus.Experimental; -import org.jetbrains.annotations.NotNull; - import java.math.BigInteger; import java.util.Optional; +import org.jetbrains.annotations.ApiStatus.Experimental; +import org.jetbrains.annotations.NotNull; /** * EtherScan - API Descriptions https://etherscan.io/apis#proxy diff --git a/src/main/java/io/api/etherscan/core/IStatisticApi.java b/src/main/java/io/api/etherscan/core/IStatisticApi.java index 1b7ef59..ffd633d 100644 --- a/src/main/java/io/api/etherscan/core/IStatisticApi.java +++ b/src/main/java/io/api/etherscan/core/IStatisticApi.java @@ -3,9 +3,8 @@ import io.api.etherscan.error.ApiException; import io.api.etherscan.model.Price; import io.api.etherscan.model.Supply; -import org.jetbrains.annotations.NotNull; - import java.math.BigInteger; +import org.jetbrains.annotations.NotNull; /** * EtherScan - API Descriptions https://etherscan.io/apis#stats diff --git a/src/main/java/io/api/etherscan/core/ITransactionApi.java b/src/main/java/io/api/etherscan/core/ITransactionApi.java index f545c2d..4180ff4 100644 --- a/src/main/java/io/api/etherscan/core/ITransactionApi.java +++ b/src/main/java/io/api/etherscan/core/ITransactionApi.java @@ -2,9 +2,8 @@ import io.api.etherscan.error.ApiException; import io.api.etherscan.model.Status; -import org.jetbrains.annotations.NotNull; - import java.util.Optional; +import org.jetbrains.annotations.NotNull; /** * EtherScan - API Descriptions https://etherscan.io/apis#transactions diff --git a/src/main/java/io/api/etherscan/core/impl/AccountApiProvider.java b/src/main/java/io/api/etherscan/core/impl/AccountApiProvider.java index 77d8b88..c807598 100644 --- a/src/main/java/io/api/etherscan/core/impl/AccountApiProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/AccountApiProvider.java @@ -8,19 +8,17 @@ import io.api.etherscan.model.*; import io.api.etherscan.model.utility.*; import io.api.etherscan.util.BasicUtils; -import org.jetbrains.annotations.NotNull; - import java.math.BigInteger; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; +import org.jetbrains.annotations.NotNull; /** * Account API Implementation * * @see IAccountApi - * * @author GoodforGod * @since 28.10.2018 */ @@ -148,7 +146,8 @@ public List txs(final String address, final long startBlock, final long endB * @return List of T values */ private List getRequestUsingOffset(final String urlParams, - Class tClass) throws ApiException { + Class tClass) + throws ApiException { final List result = new ArrayList<>(); int page = 1; while (true) { diff --git a/src/main/java/io/api/etherscan/core/impl/BasicProvider.java b/src/main/java/io/api/etherscan/core/impl/BasicProvider.java index b36f406..ada41bb 100644 --- a/src/main/java/io/api/etherscan/core/impl/BasicProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/BasicProvider.java @@ -9,7 +9,6 @@ import io.api.etherscan.manager.IQueueManager; import io.api.etherscan.model.utility.StringResponseTO; import io.api.etherscan.util.BasicUtils; - import java.time.LocalDate; import java.time.LocalDateTime; import java.util.Map; diff --git a/src/main/java/io/api/etherscan/core/impl/BlockApiProvider.java b/src/main/java/io/api/etherscan/core/impl/BlockApiProvider.java index 9f386a7..d634c9b 100644 --- a/src/main/java/io/api/etherscan/core/impl/BlockApiProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/BlockApiProvider.java @@ -7,15 +7,13 @@ import io.api.etherscan.model.UncleBlock; import io.api.etherscan.model.utility.UncleBlockResponseTO; import io.api.etherscan.util.BasicUtils; -import org.jetbrains.annotations.NotNull; - import java.util.Optional; +import org.jetbrains.annotations.NotNull; /** * Block API Implementation * * @see IBlockApi - * * @author GoodforGod * @since 28.10.2018 */ diff --git a/src/main/java/io/api/etherscan/core/impl/ContractApiProvider.java b/src/main/java/io/api/etherscan/core/impl/ContractApiProvider.java index 125087f..2e7cbea 100644 --- a/src/main/java/io/api/etherscan/core/impl/ContractApiProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/ContractApiProvider.java @@ -14,7 +14,6 @@ * Contract API Implementation * * @see IContractApi - * * @author GoodforGod * @since 28.10.2018 */ 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 ba5dd83..aac428b 100644 --- a/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java +++ b/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java @@ -10,9 +10,8 @@ import io.api.etherscan.manager.impl.QueueManager; import io.api.etherscan.model.EthNetwork; import io.api.etherscan.util.BasicUtils; -import org.jetbrains.annotations.NotNull; - import java.util.function.Supplier; +import org.jetbrains.annotations.NotNull; /** * EtherScan full API Description https://etherscan.io/apis @@ -85,7 +84,9 @@ public EtherScanApi(final String apiKey, // EtherScan 1request\5sec limit support by queue manager final IHttpExecutor executor = executorSupplier.get(); - final String ending = EthNetwork.TOBALABA.equals(network) ? "com" : "io"; + final String ending = EthNetwork.TOBALABA.equals(network) + ? "com" + : "io"; final String baseUrl = "https://" + network.getDomain() + ".etherscan." + ending + "/api" + "?apikey=" + apiKey; this.queueManager = queue; diff --git a/src/main/java/io/api/etherscan/core/impl/LogsApiProvider.java b/src/main/java/io/api/etherscan/core/impl/LogsApiProvider.java index 6086869..04f9bb7 100644 --- a/src/main/java/io/api/etherscan/core/impl/LogsApiProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/LogsApiProvider.java @@ -8,16 +8,14 @@ import io.api.etherscan.model.query.impl.LogQuery; import io.api.etherscan.model.utility.LogResponseTO; import io.api.etherscan.util.BasicUtils; -import org.jetbrains.annotations.NotNull; - import java.util.Collections; import java.util.List; +import org.jetbrains.annotations.NotNull; /** * Logs API Implementation * * @see ILogsApi - * * @author GoodforGod * @since 28.10.2018 */ diff --git a/src/main/java/io/api/etherscan/core/impl/ProxyApiProvider.java b/src/main/java/io/api/etherscan/core/impl/ProxyApiProvider.java index cb0c6a5..f456186 100644 --- a/src/main/java/io/api/etherscan/core/impl/ProxyApiProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/ProxyApiProvider.java @@ -14,17 +14,15 @@ import io.api.etherscan.model.proxy.utility.TxInfoProxyTO; import io.api.etherscan.model.proxy.utility.TxProxyTO; import io.api.etherscan.util.BasicUtils; -import org.jetbrains.annotations.NotNull; - import java.math.BigInteger; import java.util.Optional; import java.util.regex.Pattern; +import org.jetbrains.annotations.NotNull; /** * Proxy API Implementation * * @see IProxyApi - * * @author GoodforGod * @since 28.10.2018 */ @@ -109,7 +107,9 @@ public Optional tx(final String txhash) throws ApiException { @Override public Optional tx(final long blockNo, final long index) throws ApiException { final long compBlockNo = BasicUtils.compensateMinBlock(blockNo); - final long compIndex = (index < 1) ? 1 : index; + final long compIndex = (index < 1) + ? 1 + : index; final String urlParams = ACT_TX_BY_BLOCKNOINDEX_PARAM + TAG_PARAM + compBlockNo + INDEX_PARAM + "0x" + Long.toHexString(compIndex); diff --git a/src/main/java/io/api/etherscan/core/impl/StatisticApiProvider.java b/src/main/java/io/api/etherscan/core/impl/StatisticApiProvider.java index d178a81..a14119a 100644 --- a/src/main/java/io/api/etherscan/core/impl/StatisticApiProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/StatisticApiProvider.java @@ -10,15 +10,13 @@ import io.api.etherscan.model.utility.PriceResponseTO; import io.api.etherscan.model.utility.StringResponseTO; import io.api.etherscan.util.BasicUtils; -import org.jetbrains.annotations.NotNull; - import java.math.BigInteger; +import org.jetbrains.annotations.NotNull; /** * Statistic API Implementation * * @see IStatisticApi - * * @author GoodforGod * @since 28.10.2018 */ diff --git a/src/main/java/io/api/etherscan/core/impl/TransactionApiProvider.java b/src/main/java/io/api/etherscan/core/impl/TransactionApiProvider.java index 82eb467..1c83bf0 100644 --- a/src/main/java/io/api/etherscan/core/impl/TransactionApiProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/TransactionApiProvider.java @@ -8,9 +8,8 @@ import io.api.etherscan.model.utility.ReceiptStatusResponseTO; import io.api.etherscan.model.utility.StatusResponseTO; import io.api.etherscan.util.BasicUtils; -import org.jetbrains.annotations.NotNull; - import java.util.Optional; +import org.jetbrains.annotations.NotNull; /** * Transaction API Implementation diff --git a/src/main/java/io/api/etherscan/executor/impl/HttpExecutor.java b/src/main/java/io/api/etherscan/executor/impl/HttpExecutor.java index 5ba39f2..49e7fee 100644 --- a/src/main/java/io/api/etherscan/executor/impl/HttpExecutor.java +++ b/src/main/java/io/api/etherscan/executor/impl/HttpExecutor.java @@ -1,10 +1,11 @@ package io.api.etherscan.executor.impl; +import static java.net.HttpURLConnection.*; + import io.api.etherscan.error.ApiTimeoutException; import io.api.etherscan.error.ConnectionException; import io.api.etherscan.executor.IHttpExecutor; import io.api.etherscan.util.BasicUtils; - import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -18,8 +19,6 @@ import java.util.zip.GZIPInputStream; import java.util.zip.InflaterInputStream; -import static java.net.HttpURLConnection.*; - /** * Http client implementation * @@ -107,7 +106,9 @@ public String get(final String urlAsString) { public String post(final String urlAsString, final String dataToPost) { try { final HttpURLConnection connection = buildConnection(urlAsString, "POST"); - final String contentLength = (BasicUtils.isBlank(dataToPost)) ? "0" : String.valueOf(dataToPost.length()); + final String contentLength = (BasicUtils.isBlank(dataToPost)) + ? "0" + : String.valueOf(dataToPost.length()); connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); connection.setRequestProperty("Content-Length", contentLength); connection.setFixedLengthStreamingMode(dataToPost.length()); 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 764f7d5..d3a44de 100644 --- a/src/main/java/io/api/etherscan/manager/impl/QueueManager.java +++ b/src/main/java/io/api/etherscan/manager/impl/QueueManager.java @@ -1,14 +1,12 @@ package io.api.etherscan.manager.impl; import io.api.etherscan.manager.IQueueManager; - import java.util.concurrent.*; /** * Queue Semaphore implementation with size and reset time as params * * @see IQueueManager - * * @author GoodforGod * @since 30.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/Abi.java b/src/main/java/io/api/etherscan/model/Abi.java index a48a11d..880e6a0 100644 --- a/src/main/java/io/api/etherscan/model/Abi.java +++ b/src/main/java/io/api/etherscan/model/Abi.java @@ -3,8 +3,6 @@ import io.api.etherscan.util.BasicUtils; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 31.10.2018 */ @@ -49,13 +47,19 @@ public boolean equals(Object o) { if (isVerified != abi.isVerified) return false; - return contractAbi != null ? contractAbi.equals(abi.contractAbi) : abi.contractAbi == null; + return contractAbi != null + ? contractAbi.equals(abi.contractAbi) + : abi.contractAbi == null; } @Override public int hashCode() { - int result = contractAbi != null ? contractAbi.hashCode() : 0; - result = 31 * result + (isVerified ? 1 : 0); + int result = contractAbi != null + ? contractAbi.hashCode() + : 0; + result = 31 * result + (isVerified + ? 1 + : 0); return result; } diff --git a/src/main/java/io/api/etherscan/model/Balance.java b/src/main/java/io/api/etherscan/model/Balance.java index cbd8502..ed6d6c5 100644 --- a/src/main/java/io/api/etherscan/model/Balance.java +++ b/src/main/java/io/api/etherscan/model/Balance.java @@ -1,13 +1,10 @@ package io.api.etherscan.model; import io.api.etherscan.model.utility.BalanceTO; - import java.math.BigInteger; import java.util.Objects; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 28.10.2018 */ @@ -70,7 +67,9 @@ public boolean equals(Object o) { @Override public int hashCode() { int result = balance.hashCode(); - result = 31 * result + (address != null ? address.hashCode() : 0); + result = 31 * result + (address != null + ? address.hashCode() + : 0); return result; } diff --git a/src/main/java/io/api/etherscan/model/BaseTx.java b/src/main/java/io/api/etherscan/model/BaseTx.java index 6eba826..3942d14 100644 --- a/src/main/java/io/api/etherscan/model/BaseTx.java +++ b/src/main/java/io/api/etherscan/model/BaseTx.java @@ -2,15 +2,12 @@ import com.google.gson.annotations.Expose; import io.api.etherscan.util.BasicUtils; - import java.math.BigInteger; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.Objects; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 28.10.2018 */ @@ -18,7 +15,7 @@ abstract class BaseTx { private long blockNumber; private String timeStamp; - @Expose(serialize = false, deserialize = false) + @Expose(deserialize = false, serialize = false) private LocalDateTime _timeStamp; private String hash; private String from; @@ -98,11 +95,21 @@ public boolean equals(Object o) { @Override public int hashCode() { int result = (int) (blockNumber ^ (blockNumber >>> 32)); - result = 31 * result + (timeStamp != null ? timeStamp.hashCode() : 0); - result = 31 * result + (hash != null ? hash.hashCode() : 0); - result = 31 * result + (from != null ? from.hashCode() : 0); - result = 31 * result + (to != null ? to.hashCode() : 0); - result = 31 * result + (value != null ? value.hashCode() : 0); + result = 31 * result + (timeStamp != null + ? timeStamp.hashCode() + : 0); + result = 31 * result + (hash != null + ? hash.hashCode() + : 0); + result = 31 * result + (from != null + ? from.hashCode() + : 0); + result = 31 * result + (to != null + ? to.hashCode() + : 0); + result = 31 * result + (value != null + ? value.hashCode() + : 0); return result; } diff --git a/src/main/java/io/api/etherscan/model/Block.java b/src/main/java/io/api/etherscan/model/Block.java index 8853956..f5e8b6a 100644 --- a/src/main/java/io/api/etherscan/model/Block.java +++ b/src/main/java/io/api/etherscan/model/Block.java @@ -2,14 +2,11 @@ import com.google.gson.annotations.Expose; import io.api.etherscan.util.BasicUtils; - import java.math.BigInteger; import java.time.LocalDateTime; import java.time.ZoneOffset; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 28.10.2018 */ @@ -18,7 +15,7 @@ public class Block { private long blockNumber; private BigInteger blockReward; private String timeStamp; - @Expose(serialize = false, deserialize = false) + @Expose(deserialize = false, serialize = false) private LocalDateTime _timeStamp; // diff --git a/src/main/java/io/api/etherscan/model/EthNetwork.java b/src/main/java/io/api/etherscan/model/EthNetwork.java index f7b91de..6144cf1 100644 --- a/src/main/java/io/api/etherscan/model/EthNetwork.java +++ b/src/main/java/io/api/etherscan/model/EthNetwork.java @@ -1,8 +1,6 @@ package io.api.etherscan.model; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 28.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/Log.java b/src/main/java/io/api/etherscan/model/Log.java index 67ce96f..595122b 100644 --- a/src/main/java/io/api/etherscan/model/Log.java +++ b/src/main/java/io/api/etherscan/model/Log.java @@ -2,7 +2,6 @@ import com.google.gson.annotations.Expose; import io.api.etherscan.util.BasicUtils; - import java.math.BigInteger; import java.time.LocalDateTime; import java.time.ZoneOffset; @@ -10,34 +9,32 @@ import java.util.Objects; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 31.10.2018 */ public class Log { private String blockNumber; - @Expose(serialize = false, deserialize = false) + @Expose(deserialize = false, serialize = false) private Long _blockNumber; private String address; private String transactionHash; private String transactionIndex; - @Expose(serialize = false, deserialize = false) + @Expose(deserialize = false, serialize = false) private Long _transactionIndex; private String timeStamp; - @Expose(serialize = false, deserialize = false) + @Expose(deserialize = false, serialize = false) private LocalDateTime _timeStamp; private String data; private String gasPrice; - @Expose(serialize = false, deserialize = false) + @Expose(deserialize = false, serialize = false) private BigInteger _gasPrice; private String gasUsed; - @Expose(serialize = false, deserialize = false) + @Expose(deserialize = false, serialize = false) private BigInteger _gasUsed; private List topics; private String logIndex; - @Expose(serialize = false, deserialize = false) + @Expose(deserialize = false, serialize = false) private Long _logIndex; // @@ -144,11 +141,21 @@ public boolean equals(Object o) { @Override public int hashCode() { - int result = blockNumber != null ? blockNumber.hashCode() : 0; - result = 31 * result + (address != null ? address.hashCode() : 0); - result = 31 * result + (transactionHash != null ? transactionHash.hashCode() : 0); - result = 31 * result + (timeStamp != null ? timeStamp.hashCode() : 0); - result = 31 * result + (logIndex != null ? logIndex.hashCode() : 0); + int result = blockNumber != null + ? blockNumber.hashCode() + : 0; + result = 31 * result + (address != null + ? address.hashCode() + : 0); + result = 31 * result + (transactionHash != null + ? transactionHash.hashCode() + : 0); + result = 31 * result + (timeStamp != null + ? timeStamp.hashCode() + : 0); + result = 31 * result + (logIndex != null + ? logIndex.hashCode() + : 0); return result; } diff --git a/src/main/java/io/api/etherscan/model/Price.java b/src/main/java/io/api/etherscan/model/Price.java index 9bc7dc7..fc72ab5 100644 --- a/src/main/java/io/api/etherscan/model/Price.java +++ b/src/main/java/io/api/etherscan/model/Price.java @@ -1,13 +1,10 @@ package io.api.etherscan.model; import com.google.gson.annotations.Expose; - import java.time.LocalDateTime; import java.time.ZoneOffset; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 30.10.2018 */ @@ -17,9 +14,9 @@ public class Price { private double ethbtc; private String ethusd_timestamp; private String ethbtc_timestamp; - @Expose(serialize = false, deserialize = false) + @Expose(deserialize = false, serialize = false) private LocalDateTime _ethusd_timestamp; - @Expose(serialize = false, deserialize = false) + @Expose(deserialize = false, serialize = false) private LocalDateTime _ethbtc_timestamp; public double inUsd() { @@ -55,9 +52,13 @@ public boolean equals(Object o) { return false; if (Double.compare(price.ethbtc, ethbtc) != 0) return false; - if (ethusd_timestamp != null ? !ethusd_timestamp.equals(price.ethusd_timestamp) : price.ethusd_timestamp != null) + if (ethusd_timestamp != null + ? !ethusd_timestamp.equals(price.ethusd_timestamp) + : price.ethusd_timestamp != null) return false; - return (ethbtc_timestamp != null ? !ethbtc_timestamp.equals(price.ethbtc_timestamp) : price.ethbtc_timestamp != null); + return (ethbtc_timestamp != null + ? !ethbtc_timestamp.equals(price.ethbtc_timestamp) + : price.ethbtc_timestamp != null); } @Override @@ -68,8 +69,12 @@ public int hashCode() { result = (int) (temp ^ (temp >>> 32)); temp = Double.doubleToLongBits(ethbtc); result = 31 * result + (int) (temp ^ (temp >>> 32)); - result = 31 * result + (ethusd_timestamp != null ? ethusd_timestamp.hashCode() : 0); - result = 31 * result + (ethbtc_timestamp != null ? ethbtc_timestamp.hashCode() : 0); + result = 31 * result + (ethusd_timestamp != null + ? ethusd_timestamp.hashCode() + : 0); + result = 31 * result + (ethbtc_timestamp != null + ? ethbtc_timestamp.hashCode() + : 0); return result; } diff --git a/src/main/java/io/api/etherscan/model/Status.java b/src/main/java/io/api/etherscan/model/Status.java index 9683bde..2017cd7 100644 --- a/src/main/java/io/api/etherscan/model/Status.java +++ b/src/main/java/io/api/etherscan/model/Status.java @@ -41,7 +41,9 @@ public boolean equals(Object o) { @Override public int hashCode() { int result = isError; - result = 31 * result + (errDescription != null ? errDescription.hashCode() : 0); + result = 31 * result + (errDescription != null + ? errDescription.hashCode() + : 0); return result; } diff --git a/src/main/java/io/api/etherscan/model/Supply.java b/src/main/java/io/api/etherscan/model/Supply.java index 2fd6db7..f495aaf 100644 --- a/src/main/java/io/api/etherscan/model/Supply.java +++ b/src/main/java/io/api/etherscan/model/Supply.java @@ -3,8 +3,6 @@ import java.math.BigInteger; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 30.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/TokenBalance.java b/src/main/java/io/api/etherscan/model/TokenBalance.java index d057992..684738c 100644 --- a/src/main/java/io/api/etherscan/model/TokenBalance.java +++ b/src/main/java/io/api/etherscan/model/TokenBalance.java @@ -4,8 +4,6 @@ import java.util.Objects; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 31.10.2018 */ @@ -38,7 +36,9 @@ public boolean equals(Object o) { @Override public int hashCode() { int result = super.hashCode(); - result = 31 * result + (tokenContract != null ? tokenContract.hashCode() : 0); + result = 31 * result + (tokenContract != null + ? tokenContract.hashCode() + : 0); return result; } diff --git a/src/main/java/io/api/etherscan/model/Tx.java b/src/main/java/io/api/etherscan/model/Tx.java index 4136d23..13b5292 100644 --- a/src/main/java/io/api/etherscan/model/Tx.java +++ b/src/main/java/io/api/etherscan/model/Tx.java @@ -1,13 +1,10 @@ package io.api.etherscan.model; import io.api.etherscan.util.BasicUtils; - import java.math.BigInteger; import java.util.Objects; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 28.10.2018 */ @@ -80,9 +77,13 @@ public boolean equals(Object o) { public int hashCode() { int result = super.hashCode(); result = 31 * result + (int) (nonce ^ (nonce >>> 32)); - result = 31 * result + (blockHash != null ? blockHash.hashCode() : 0); + result = 31 * result + (blockHash != null + ? blockHash.hashCode() + : 0); result = 31 * result + transactionIndex; - result = 31 * result + (isError != null ? isError.hashCode() : 0); + result = 31 * result + (isError != null + ? isError.hashCode() + : 0); return result; } diff --git a/src/main/java/io/api/etherscan/model/TxInternal.java b/src/main/java/io/api/etherscan/model/TxInternal.java index 5048947..5471268 100644 --- a/src/main/java/io/api/etherscan/model/TxInternal.java +++ b/src/main/java/io/api/etherscan/model/TxInternal.java @@ -3,8 +3,6 @@ import java.util.Objects; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 29.10.2018 */ @@ -21,7 +19,9 @@ public String getType() { } public long getTraceId() { - return (traceId == null) ? 0 : Long.parseLong(traceId); + return (traceId == null) + ? 0 + : Long.parseLong(traceId); } public String getTraceIdAsString() { @@ -56,8 +56,12 @@ public boolean equals(Object o) { @Override public int hashCode() { int result = super.hashCode(); - result = 31 * result + (traceId != null ? traceId.hashCode() : 0); - result = 31 * result + (errCode != null ? errCode.hashCode() : 0); + result = 31 * result + (traceId != null + ? traceId.hashCode() + : 0); + result = 31 * result + (errCode != null + ? errCode.hashCode() + : 0); return result; } diff --git a/src/main/java/io/api/etherscan/model/TxToken.java b/src/main/java/io/api/etherscan/model/TxToken.java index 8f5e36f..c455ffb 100644 --- a/src/main/java/io/api/etherscan/model/TxToken.java +++ b/src/main/java/io/api/etherscan/model/TxToken.java @@ -1,8 +1,6 @@ package io.api.etherscan.model; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 28.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/Uncle.java b/src/main/java/io/api/etherscan/model/Uncle.java index 2ee206b..7dea648 100644 --- a/src/main/java/io/api/etherscan/model/Uncle.java +++ b/src/main/java/io/api/etherscan/model/Uncle.java @@ -3,8 +3,6 @@ import java.math.BigInteger; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 30.10.2018 */ @@ -39,15 +37,23 @@ public boolean equals(Object o) { if (unclePosition != uncle.unclePosition) return false; - if (miner != null ? !miner.equals(uncle.miner) : uncle.miner != null) + if (miner != null + ? !miner.equals(uncle.miner) + : uncle.miner != null) return false; - return blockreward != null ? blockreward.equals(uncle.blockreward) : uncle.blockreward == null; + return blockreward != null + ? blockreward.equals(uncle.blockreward) + : uncle.blockreward == null; } @Override public int hashCode() { - int result = miner != null ? miner.hashCode() : 0; - result = 31 * result + (blockreward != null ? blockreward.hashCode() : 0); + int result = miner != null + ? miner.hashCode() + : 0; + result = 31 * result + (blockreward != null + ? blockreward.hashCode() + : 0); result = 31 * result + unclePosition; return result; } diff --git a/src/main/java/io/api/etherscan/model/UncleBlock.java b/src/main/java/io/api/etherscan/model/UncleBlock.java index 88c975d..ff30451 100644 --- a/src/main/java/io/api/etherscan/model/UncleBlock.java +++ b/src/main/java/io/api/etherscan/model/UncleBlock.java @@ -1,12 +1,9 @@ package io.api.etherscan.model; import io.api.etherscan.util.BasicUtils; - import java.util.List; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 30.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/Wei.java b/src/main/java/io/api/etherscan/model/Wei.java index eddf8d2..0735d90 100644 --- a/src/main/java/io/api/etherscan/model/Wei.java +++ b/src/main/java/io/api/etherscan/model/Wei.java @@ -4,8 +4,6 @@ import java.util.Objects; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 30.10.2018 */ @@ -52,7 +50,9 @@ public boolean equals(Object o) { @Override public int hashCode() { - return result != null ? result.hashCode() : 0; + return result != null + ? result.hashCode() + : 0; } @Override diff --git a/src/main/java/io/api/etherscan/model/proxy/BlockProxy.java b/src/main/java/io/api/etherscan/model/proxy/BlockProxy.java index 63821c0..2afbe40 100644 --- a/src/main/java/io/api/etherscan/model/proxy/BlockProxy.java +++ b/src/main/java/io/api/etherscan/model/proxy/BlockProxy.java @@ -2,33 +2,30 @@ import com.google.gson.annotations.Expose; import io.api.etherscan.util.BasicUtils; - import java.math.BigInteger; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.List; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 31.10.2018 */ public class BlockProxy { private String number; - @Expose(serialize = false, deserialize = false) + @Expose(deserialize = false, serialize = false) private Long _number; private String hash; private String parentHash; private String stateRoot; private String size; - @Expose(serialize = false, deserialize = false) + @Expose(deserialize = false, serialize = false) private Long _size; private String difficulty; private String totalDifficulty; private String timestamp; - @Expose(serialize = false, deserialize = false) + @Expose(deserialize = false, serialize = false) private LocalDateTime _timestamp; private String miner; @@ -37,10 +34,10 @@ public class BlockProxy { private String logsBloom; private String mixHash; private String gasUsed; - @Expose(serialize = false, deserialize = false) + @Expose(deserialize = false, serialize = false) private BigInteger _gasUsed; private String gasLimit; - @Expose(serialize = false, deserialize = false) + @Expose(deserialize = false, serialize = false) private BigInteger _gasLimit; private String sha3Uncles; @@ -151,18 +148,30 @@ public boolean equals(Object o) { BlockProxy that = (BlockProxy) o; - if (number != null ? !number.equals(that.number) : that.number != null) + if (number != null + ? !number.equals(that.number) + : that.number != null) return false; - if (hash != null ? !hash.equals(that.hash) : that.hash != null) + if (hash != null + ? !hash.equals(that.hash) + : that.hash != null) return false; - return parentHash != null ? parentHash.equals(that.parentHash) : that.parentHash == null; + return parentHash != null + ? parentHash.equals(that.parentHash) + : that.parentHash == null; } @Override public int hashCode() { - int result = number != null ? number.hashCode() : 0; - result = 31 * result + (hash != null ? hash.hashCode() : 0); - result = 31 * result + (parentHash != null ? parentHash.hashCode() : 0); + int result = number != null + ? number.hashCode() + : 0; + result = 31 * result + (hash != null + ? hash.hashCode() + : 0); + result = 31 * result + (parentHash != null + ? parentHash.hashCode() + : 0); return result; } diff --git a/src/main/java/io/api/etherscan/model/proxy/ReceiptProxy.java b/src/main/java/io/api/etherscan/model/proxy/ReceiptProxy.java index f40cb59..1e25dbd 100644 --- a/src/main/java/io/api/etherscan/model/proxy/ReceiptProxy.java +++ b/src/main/java/io/api/etherscan/model/proxy/ReceiptProxy.java @@ -3,13 +3,10 @@ import com.google.gson.annotations.Expose; import io.api.etherscan.model.Log; import io.api.etherscan.util.BasicUtils; - import java.math.BigInteger; import java.util.List; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ @@ -104,18 +101,30 @@ public boolean equals(Object o) { ReceiptProxy that = (ReceiptProxy) o; - if (blockNumber != null ? !blockNumber.equals(that.blockNumber) : that.blockNumber != null) + if (blockNumber != null + ? !blockNumber.equals(that.blockNumber) + : that.blockNumber != null) return false; - if (transactionHash != null ? !transactionHash.equals(that.transactionHash) : that.transactionHash != null) + if (transactionHash != null + ? !transactionHash.equals(that.transactionHash) + : that.transactionHash != null) return false; - return transactionIndex != null ? transactionIndex.equals(that.transactionIndex) : that.transactionIndex == null; + return transactionIndex != null + ? transactionIndex.equals(that.transactionIndex) + : that.transactionIndex == null; } @Override public int hashCode() { - int result = blockNumber != null ? blockNumber.hashCode() : 0; - result = 31 * result + (transactionHash != null ? transactionHash.hashCode() : 0); - result = 31 * result + (transactionIndex != null ? transactionIndex.hashCode() : 0); + int result = blockNumber != null + ? blockNumber.hashCode() + : 0; + result = 31 * result + (transactionHash != null + ? transactionHash.hashCode() + : 0); + result = 31 * result + (transactionIndex != null + ? transactionIndex.hashCode() + : 0); return result; } diff --git a/src/main/java/io/api/etherscan/model/proxy/TxProxy.java b/src/main/java/io/api/etherscan/model/proxy/TxProxy.java index 5c7b5c8..a89f4a8 100644 --- a/src/main/java/io/api/etherscan/model/proxy/TxProxy.java +++ b/src/main/java/io/api/etherscan/model/proxy/TxProxy.java @@ -2,12 +2,9 @@ import com.google.gson.annotations.Expose; import io.api.etherscan.util.BasicUtils; - import java.math.BigInteger; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 31.10.2018 */ @@ -16,7 +13,7 @@ public class TxProxy { private String to; private String hash; private String transactionIndex; - @Expose(serialize = false, deserialize = false) + @Expose(deserialize = false, serialize = false) private Long _transactionIndex; private String from; private String v; @@ -24,18 +21,18 @@ public class TxProxy { private String s; private String r; private String nonce; - @Expose(serialize = false, deserialize = false) + @Expose(deserialize = false, serialize = false) private Long _nonce; private String value; private String gas; - @Expose(serialize = false, deserialize = false) + @Expose(deserialize = false, serialize = false) private BigInteger _gas; private String gasPrice; - @Expose(serialize = false, deserialize = false) + @Expose(deserialize = false, serialize = false) private BigInteger _gasPrice; private String blockHash; private String blockNumber; - @Expose(serialize = false, deserialize = false) + @Expose(deserialize = false, serialize = false) private Long _blockNumber; // @@ -115,18 +112,30 @@ public boolean equals(Object o) { TxProxy txProxy = (TxProxy) o; - if (hash != null ? !hash.equals(txProxy.hash) : txProxy.hash != null) + if (hash != null + ? !hash.equals(txProxy.hash) + : txProxy.hash != null) return false; - if (blockHash != null ? !blockHash.equals(txProxy.blockHash) : txProxy.blockHash != null) + if (blockHash != null + ? !blockHash.equals(txProxy.blockHash) + : txProxy.blockHash != null) return false; - return blockNumber != null ? blockNumber.equals(txProxy.blockNumber) : txProxy.blockNumber == null; + return blockNumber != null + ? blockNumber.equals(txProxy.blockNumber) + : txProxy.blockNumber == null; } @Override public int hashCode() { - int result = hash != null ? hash.hashCode() : 0; - result = 31 * result + (blockHash != null ? blockHash.hashCode() : 0); - result = 31 * result + (blockNumber != null ? blockNumber.hashCode() : 0); + int result = hash != null + ? hash.hashCode() + : 0; + result = 31 * result + (blockHash != null + ? blockHash.hashCode() + : 0); + result = 31 * result + (blockNumber != null + ? blockNumber.hashCode() + : 0); return result; } diff --git a/src/main/java/io/api/etherscan/model/proxy/utility/BaseProxyTO.java b/src/main/java/io/api/etherscan/model/proxy/utility/BaseProxyTO.java index 52c886f..0291dfe 100644 --- a/src/main/java/io/api/etherscan/model/proxy/utility/BaseProxyTO.java +++ b/src/main/java/io/api/etherscan/model/proxy/utility/BaseProxyTO.java @@ -1,8 +1,6 @@ package io.api.etherscan.model.proxy.utility; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 31.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/proxy/utility/BlockProxyTO.java b/src/main/java/io/api/etherscan/model/proxy/utility/BlockProxyTO.java index eb9d941..2057c89 100644 --- a/src/main/java/io/api/etherscan/model/proxy/utility/BlockProxyTO.java +++ b/src/main/java/io/api/etherscan/model/proxy/utility/BlockProxyTO.java @@ -3,8 +3,6 @@ import io.api.etherscan.model.proxy.BlockProxy; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 01.11.2018 */ diff --git a/src/main/java/io/api/etherscan/model/proxy/utility/ErrorProxyTO.java b/src/main/java/io/api/etherscan/model/proxy/utility/ErrorProxyTO.java index 57d2c07..a3bc435 100644 --- a/src/main/java/io/api/etherscan/model/proxy/utility/ErrorProxyTO.java +++ b/src/main/java/io/api/etherscan/model/proxy/utility/ErrorProxyTO.java @@ -1,8 +1,6 @@ package io.api.etherscan.model.proxy.utility; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ diff --git a/src/main/java/io/api/etherscan/model/proxy/utility/StringProxyTO.java b/src/main/java/io/api/etherscan/model/proxy/utility/StringProxyTO.java index 90cd7c8..8d1d08c 100644 --- a/src/main/java/io/api/etherscan/model/proxy/utility/StringProxyTO.java +++ b/src/main/java/io/api/etherscan/model/proxy/utility/StringProxyTO.java @@ -1,8 +1,6 @@ package io.api.etherscan.model.proxy.utility; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 31.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/proxy/utility/TxInfoProxyTO.java b/src/main/java/io/api/etherscan/model/proxy/utility/TxInfoProxyTO.java index c709f76..3bbe039 100644 --- a/src/main/java/io/api/etherscan/model/proxy/utility/TxInfoProxyTO.java +++ b/src/main/java/io/api/etherscan/model/proxy/utility/TxInfoProxyTO.java @@ -3,8 +3,6 @@ import io.api.etherscan.model.proxy.ReceiptProxy; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ diff --git a/src/main/java/io/api/etherscan/model/proxy/utility/TxProxyTO.java b/src/main/java/io/api/etherscan/model/proxy/utility/TxProxyTO.java index 4140a62..7e9c9e8 100644 --- a/src/main/java/io/api/etherscan/model/proxy/utility/TxProxyTO.java +++ b/src/main/java/io/api/etherscan/model/proxy/utility/TxProxyTO.java @@ -3,8 +3,6 @@ import io.api.etherscan.model.proxy.TxProxy; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 01.11.2018 */ diff --git a/src/main/java/io/api/etherscan/model/query/impl/BaseLogQuery.java b/src/main/java/io/api/etherscan/model/query/impl/BaseLogQuery.java index 2fc688a..c472f84 100644 --- a/src/main/java/io/api/etherscan/model/query/impl/BaseLogQuery.java +++ b/src/main/java/io/api/etherscan/model/query/impl/BaseLogQuery.java @@ -7,7 +7,6 @@ * * @see LogQueryBuilder * @see ILogsApi - * * @author GoodforGod * @since 31.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/query/impl/LogQuery.java b/src/main/java/io/api/etherscan/model/query/impl/LogQuery.java index 3ba6c4f..31d8c13 100644 --- a/src/main/java/io/api/etherscan/model/query/impl/LogQuery.java +++ b/src/main/java/io/api/etherscan/model/query/impl/LogQuery.java @@ -4,12 +4,10 @@ /** * Final builded container for The Event Log API - * * EtherScan - API Descriptions https://etherscan.io/apis#logs * * @see LogQueryBuilder * @see ILogsApi - * * @author GoodforGod * @since 31.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/query/impl/LogQueryBuilder.java b/src/main/java/io/api/etherscan/model/query/impl/LogQueryBuilder.java index bd8a9fc..44ca825 100644 --- a/src/main/java/io/api/etherscan/model/query/impl/LogQueryBuilder.java +++ b/src/main/java/io/api/etherscan/model/query/impl/LogQueryBuilder.java @@ -9,7 +9,6 @@ * Builder for The Event Log API * * @see ILogsApi - * * @author GoodforGod * @since 31.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/query/impl/LogTopicQuadro.java b/src/main/java/io/api/etherscan/model/query/impl/LogTopicQuadro.java index 1c2bf35..bab5b29 100644 --- a/src/main/java/io/api/etherscan/model/query/impl/LogTopicQuadro.java +++ b/src/main/java/io/api/etherscan/model/query/impl/LogTopicQuadro.java @@ -10,7 +10,6 @@ * * @see LogQueryBuilder * @see ILogsApi - * * @author GoodforGod * @since 31.10.2018 */ @@ -22,8 +21,13 @@ public class LogTopicQuadro extends BaseLogQuery implements IQueryBuilder { private LogOp topic0_1_opr, topic1_2_opr, topic2_3_opr, topic0_2_opr, topic0_3_opr, topic1_3_opr; - LogTopicQuadro(String address, long startBlock, long endBlock, - String topic0, String topic1, String topic2, String topic3) { + LogTopicQuadro(String address, + long startBlock, + long endBlock, + String topic0, + String topic1, + String topic2, + String topic3) { this.address = address; this.startBlock = startBlock; this.endBlock = endBlock; diff --git a/src/main/java/io/api/etherscan/model/query/impl/LogTopicSingle.java b/src/main/java/io/api/etherscan/model/query/impl/LogTopicSingle.java index 2c19d61..83199d9 100644 --- a/src/main/java/io/api/etherscan/model/query/impl/LogTopicSingle.java +++ b/src/main/java/io/api/etherscan/model/query/impl/LogTopicSingle.java @@ -9,7 +9,6 @@ * * @see LogQueryBuilder * @see ILogsApi - * * @author GoodforGod * @since 31.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/query/impl/LogTopicTriple.java b/src/main/java/io/api/etherscan/model/query/impl/LogTopicTriple.java index aa54740..cc9a6ba 100644 --- a/src/main/java/io/api/etherscan/model/query/impl/LogTopicTriple.java +++ b/src/main/java/io/api/etherscan/model/query/impl/LogTopicTriple.java @@ -10,7 +10,6 @@ * * @see LogQueryBuilder * @see ILogsApi - * * @author GoodforGod * @since 31.10.2018 */ @@ -22,8 +21,12 @@ public class LogTopicTriple extends BaseLogQuery implements IQueryBuilder { private LogOp topic0_1_opr, topic1_2_opr, topic0_2_opr; - LogTopicTriple(String address, long startBlock, long endBlock, - String topic0, String topic1, String topic2) { + LogTopicTriple(String address, + long startBlock, + long endBlock, + String topic0, + String topic1, + String topic2) { this.address = address; this.startBlock = startBlock; this.endBlock = endBlock; diff --git a/src/main/java/io/api/etherscan/model/query/impl/LogTopicTuple.java b/src/main/java/io/api/etherscan/model/query/impl/LogTopicTuple.java index 8f069f1..4524a8a 100644 --- a/src/main/java/io/api/etherscan/model/query/impl/LogTopicTuple.java +++ b/src/main/java/io/api/etherscan/model/query/impl/LogTopicTuple.java @@ -10,7 +10,6 @@ * * @see LogQueryBuilder * @see ILogsApi - * * @author GoodforGod * @since 31.10.2018 */ @@ -22,8 +21,11 @@ public class LogTopicTuple extends BaseLogQuery implements IQueryBuilder { private LogOp topic0_1_opr; - LogTopicTuple(String address, long startBlock, long endBlock, - String topic0, String topic1) { + LogTopicTuple(String address, + long startBlock, + long endBlock, + String topic0, + String topic1) { this.address = address; this.startBlock = startBlock; this.endBlock = endBlock; diff --git a/src/main/java/io/api/etherscan/model/utility/BalanceResponseTO.java b/src/main/java/io/api/etherscan/model/utility/BalanceResponseTO.java index 6b23de4..f7c2985 100644 --- a/src/main/java/io/api/etherscan/model/utility/BalanceResponseTO.java +++ b/src/main/java/io/api/etherscan/model/utility/BalanceResponseTO.java @@ -1,8 +1,6 @@ package io.api.etherscan.model.utility; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 29.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/utility/BalanceTO.java b/src/main/java/io/api/etherscan/model/utility/BalanceTO.java index 8d9d9b7..3956cec 100644 --- a/src/main/java/io/api/etherscan/model/utility/BalanceTO.java +++ b/src/main/java/io/api/etherscan/model/utility/BalanceTO.java @@ -1,8 +1,6 @@ package io.api.etherscan.model.utility; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 29.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/utility/BaseListResponseTO.java b/src/main/java/io/api/etherscan/model/utility/BaseListResponseTO.java index 28f01f3..916739e 100644 --- a/src/main/java/io/api/etherscan/model/utility/BaseListResponseTO.java +++ b/src/main/java/io/api/etherscan/model/utility/BaseListResponseTO.java @@ -3,8 +3,6 @@ import java.util.List; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 30.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/utility/BaseResponseTO.java b/src/main/java/io/api/etherscan/model/utility/BaseResponseTO.java index d3653e2..9679ebb 100644 --- a/src/main/java/io/api/etherscan/model/utility/BaseResponseTO.java +++ b/src/main/java/io/api/etherscan/model/utility/BaseResponseTO.java @@ -3,8 +3,6 @@ import io.api.etherscan.util.BasicUtils; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 29.10.2018 */ @@ -14,7 +12,9 @@ public abstract class BaseResponseTO { private String message; public int getStatus() { - return BasicUtils.isEmpty(status) ? -1 : Integer.parseInt(status); + return BasicUtils.isEmpty(status) + ? -1 + : Integer.parseInt(status); } public String getMessage() { diff --git a/src/main/java/io/api/etherscan/model/utility/BlockParam.java b/src/main/java/io/api/etherscan/model/utility/BlockParam.java index 0f027ec..7e11a00 100644 --- a/src/main/java/io/api/etherscan/model/utility/BlockParam.java +++ b/src/main/java/io/api/etherscan/model/utility/BlockParam.java @@ -1,8 +1,6 @@ package io.api.etherscan.model.utility; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 31.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/utility/BlockResponseTO.java b/src/main/java/io/api/etherscan/model/utility/BlockResponseTO.java index 0d63184..8a89321 100644 --- a/src/main/java/io/api/etherscan/model/utility/BlockResponseTO.java +++ b/src/main/java/io/api/etherscan/model/utility/BlockResponseTO.java @@ -3,8 +3,6 @@ import io.api.etherscan.model.Block; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 30.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/utility/LogResponseTO.java b/src/main/java/io/api/etherscan/model/utility/LogResponseTO.java index bba1c24..a060bd3 100644 --- a/src/main/java/io/api/etherscan/model/utility/LogResponseTO.java +++ b/src/main/java/io/api/etherscan/model/utility/LogResponseTO.java @@ -3,8 +3,6 @@ import io.api.etherscan.model.Log; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 31.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/utility/PriceResponseTO.java b/src/main/java/io/api/etherscan/model/utility/PriceResponseTO.java index 3179a73..9af743b 100644 --- a/src/main/java/io/api/etherscan/model/utility/PriceResponseTO.java +++ b/src/main/java/io/api/etherscan/model/utility/PriceResponseTO.java @@ -3,8 +3,6 @@ import io.api.etherscan.model.Price; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 30.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/utility/ReceiptStatusResponseTO.java b/src/main/java/io/api/etherscan/model/utility/ReceiptStatusResponseTO.java index 87e3950..a5f9577 100644 --- a/src/main/java/io/api/etherscan/model/utility/ReceiptStatusResponseTO.java +++ b/src/main/java/io/api/etherscan/model/utility/ReceiptStatusResponseTO.java @@ -1,8 +1,6 @@ package io.api.etherscan.model.utility; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ diff --git a/src/main/java/io/api/etherscan/model/utility/ReceiptStatusTO.java b/src/main/java/io/api/etherscan/model/utility/ReceiptStatusTO.java index 6b7995d..c4c63af 100644 --- a/src/main/java/io/api/etherscan/model/utility/ReceiptStatusTO.java +++ b/src/main/java/io/api/etherscan/model/utility/ReceiptStatusTO.java @@ -1,8 +1,6 @@ package io.api.etherscan.model.utility; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ diff --git a/src/main/java/io/api/etherscan/model/utility/StatusResponseTO.java b/src/main/java/io/api/etherscan/model/utility/StatusResponseTO.java index bc10eb7..7532aba 100644 --- a/src/main/java/io/api/etherscan/model/utility/StatusResponseTO.java +++ b/src/main/java/io/api/etherscan/model/utility/StatusResponseTO.java @@ -3,8 +3,6 @@ import io.api.etherscan.model.Status; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 30.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/utility/StringResponseTO.java b/src/main/java/io/api/etherscan/model/utility/StringResponseTO.java index 38d3c86..582087a 100644 --- a/src/main/java/io/api/etherscan/model/utility/StringResponseTO.java +++ b/src/main/java/io/api/etherscan/model/utility/StringResponseTO.java @@ -1,8 +1,6 @@ package io.api.etherscan.model.utility; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 29.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/utility/TxInternalResponseTO.java b/src/main/java/io/api/etherscan/model/utility/TxInternalResponseTO.java index d38a879..5f0e400 100644 --- a/src/main/java/io/api/etherscan/model/utility/TxInternalResponseTO.java +++ b/src/main/java/io/api/etherscan/model/utility/TxInternalResponseTO.java @@ -3,8 +3,6 @@ import io.api.etherscan.model.TxInternal; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 29.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/utility/TxResponseTO.java b/src/main/java/io/api/etherscan/model/utility/TxResponseTO.java index 53cce38..1fa6b16 100644 --- a/src/main/java/io/api/etherscan/model/utility/TxResponseTO.java +++ b/src/main/java/io/api/etherscan/model/utility/TxResponseTO.java @@ -3,8 +3,6 @@ import io.api.etherscan.model.Tx; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 29.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/utility/TxTokenResponseTO.java b/src/main/java/io/api/etherscan/model/utility/TxTokenResponseTO.java index 5ac2aec..1cbd4e3 100644 --- a/src/main/java/io/api/etherscan/model/utility/TxTokenResponseTO.java +++ b/src/main/java/io/api/etherscan/model/utility/TxTokenResponseTO.java @@ -3,8 +3,6 @@ import io.api.etherscan.model.TxToken; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 29.10.2018 */ diff --git a/src/main/java/io/api/etherscan/model/utility/UncleBlockResponseTO.java b/src/main/java/io/api/etherscan/model/utility/UncleBlockResponseTO.java index f4f4349..f8e4c5e 100644 --- a/src/main/java/io/api/etherscan/model/utility/UncleBlockResponseTO.java +++ b/src/main/java/io/api/etherscan/model/utility/UncleBlockResponseTO.java @@ -3,8 +3,6 @@ import io.api.etherscan.model.UncleBlock; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 30.10.2018 */ diff --git a/src/main/java/io/api/etherscan/util/BasicUtils.java b/src/main/java/io/api/etherscan/util/BasicUtils.java index 96b855d..d748abf 100644 --- a/src/main/java/io/api/etherscan/util/BasicUtils.java +++ b/src/main/java/io/api/etherscan/util/BasicUtils.java @@ -5,11 +5,10 @@ import io.api.etherscan.error.InvalidTxHashException; import io.api.etherscan.model.utility.BaseResponseTO; import io.api.etherscan.model.utility.BlockParam; -import org.jetbrains.annotations.NotNull; - import java.math.BigInteger; import java.util.*; import java.util.regex.Pattern; +import org.jetbrains.annotations.NotNull; /** * Basic utils for library diff --git a/src/test/java/io/api/ApiRunner.java b/src/test/java/io/api/ApiRunner.java index 184a84e..e78ea6d 100644 --- a/src/test/java/io/api/ApiRunner.java +++ b/src/test/java/io/api/ApiRunner.java @@ -3,10 +3,10 @@ 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; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; -public class ApiRunner extends Assert { +public class ApiRunner extends Assertions { private static final EtherScanApi api; private static final EtherScanApi apiRopsten; @@ -50,7 +50,7 @@ public static EtherScanApi getApiKovan() { return apiKovan; } - @AfterClass + @AfterAll public static void cleanup() throws Exception { api.close(); apiRopsten.close(); diff --git a/src/test/java/io/api/etherscan/EtherScanApiTest.java b/src/test/java/io/api/etherscan/EtherScanApiTest.java index be49435..b649302 100644 --- a/src/test/java/io/api/etherscan/EtherScanApiTest.java +++ b/src/test/java/io/api/etherscan/EtherScanApiTest.java @@ -8,47 +8,43 @@ import io.api.etherscan.executor.IHttpExecutor; import io.api.etherscan.executor.impl.HttpExecutor; import io.api.etherscan.model.Balance; -import io.api.etherscan.model.Block; import io.api.etherscan.model.EthNetwork; -import org.junit.Test; - -import java.util.List; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; +import org.junit.jupiter.api.Test; /** * @author GoodforGod * @since 05.11.2018 */ -public class EtherScanApiTest extends ApiRunner { +class EtherScanApiTest extends ApiRunner { private final EthNetwork network = EthNetwork.KOVAN; private final String validKey = "YourKey"; @Test - public void validKey() { + void validKey() { EtherScanApi api = new EtherScanApi(validKey, network); assertNotNull(api); } - @Test(expected = ApiKeyException.class) - public void emptyKey() { - new EtherScanApi(""); + @Test + void emptyKey() { + assertThrows(ApiKeyException.class, () -> new EtherScanApi("")); } - @Test(expected = ApiKeyException.class) - public void blankKey() { - new EtherScanApi(" ", network); + @Test + void blankKey() { + assertThrows(ApiKeyException.class, () -> new EtherScanApi(" ", network)); } - @Test(expected = ApiException.class) - public void nullNetwork() { - EtherScanApi api = new EtherScanApi(validKey, null); - assertNotNull(api); + @Test + void nullNetwork() { + assertThrows(ApiException.class, () -> new EtherScanApi(validKey, null)); } @Test - public void noTimeoutOnRead() { + void noTimeoutOnRead() { Supplier supplier = () -> new HttpExecutor(300); EtherScanApi api = new EtherScanApi(EthNetwork.MAINNET, supplier); Balance balance = api.account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); @@ -56,31 +52,30 @@ public void noTimeoutOnRead() { } @Test - public void noTimeoutOnReadGroli() { + void noTimeoutOnReadGroli() { Supplier supplier = () -> new HttpExecutor(300); Balance balance = getApi().account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); assertNotNull(balance); } @Test - public void noTimeoutOnReadTobalala() { + void noTimeoutOnReadTobalala() { Supplier supplier = () -> new HttpExecutor(30000); Balance balance = getApi().account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); assertNotNull(balance); } @Test - public void noTimeoutUnlimitedAwait() { + void noTimeoutUnlimitedAwait() { Balance balance = getApi().account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); assertNotNull(balance); } - @Test(expected = ApiTimeoutException.class) - public void timeout() throws InterruptedException { + @Test + void timeout() throws InterruptedException { TimeUnit.SECONDS.sleep(5); Supplier supplier = () -> new HttpExecutor(300, 300); EtherScanApi api = new EtherScanApi(getApiKey(), EthNetwork.KOVAN, supplier); - List blocks = api.account().minedBlocks("0x0010f94b296A852aAac52EA6c5Ac72e03afD032D"); - assertNotNull(blocks); + assertThrows(ApiTimeoutException.class, () -> api.account().minedBlocks("0x0010f94b296A852aAac52EA6c5Ac72e03afD032D")); } } diff --git a/src/test/java/io/api/etherscan/account/AccountBalanceListTest.java b/src/test/java/io/api/etherscan/account/AccountBalanceListTest.java index fdeb1e9..6864175 100644 --- a/src/test/java/io/api/etherscan/account/AccountBalanceListTest.java +++ b/src/test/java/io/api/etherscan/account/AccountBalanceListTest.java @@ -4,22 +4,19 @@ import io.api.etherscan.error.InvalidAddressException; import io.api.etherscan.model.Balance; import io.api.support.AddressUtil; -import org.junit.Test; - import java.math.BigInteger; import java.util.ArrayList; import java.util.List; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class AccountBalanceListTest extends ApiRunner { +class AccountBalanceListTest extends ApiRunner { @Test - public void correct() { + void correct() { List addresses = new ArrayList<>(); addresses.add("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9"); addresses.add("0xC9F32CE1127e44C51cbD182D6364F3D707Fd0d47"); @@ -44,7 +41,7 @@ public void correct() { } @Test - public void correctMoreThat20Addresses() { + void correctMoreThat20Addresses() { List addresses = AddressUtil.genRealAddresses(); List balances = getApi().account().balances(addresses); @@ -58,17 +55,17 @@ public void correctMoreThat20Addresses() { assertNotEquals(balances.get(0), balances.get(1)); } - @Test(expected = InvalidAddressException.class) - public void invalidParamWithError() { + @Test + void invalidParamWithError() { List addresses = new ArrayList<>(); addresses.add("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9"); addresses.add("C9F32CE1127e44C51cbD182D6364F3D707Fd0d47"); - getApi().account().balances(addresses); + assertThrows(InvalidAddressException.class, () -> getApi().account().balances(addresses)); } @Test - public void emptyParamList() { + void emptyParamList() { List addresses = new ArrayList<>(); List balances = getApi().account().balances(addresses); assertNotNull(balances); @@ -76,7 +73,7 @@ public void emptyParamList() { } @Test - public void correctParamWithEmptyExpectedResult() { + void correctParamWithEmptyExpectedResult() { List addresses = new ArrayList<>(); addresses.add("0x1327cb34984c3992ec1EA0eAE98Ccf80A74f95B9"); addresses.add("0xC1F32CE1127e44C51cbD182D6364F3D707Fd0d47"); diff --git a/src/test/java/io/api/etherscan/account/AccountBalanceTest.java b/src/test/java/io/api/etherscan/account/AccountBalanceTest.java index 76aca68..d5427ab 100644 --- a/src/test/java/io/api/etherscan/account/AccountBalanceTest.java +++ b/src/test/java/io/api/etherscan/account/AccountBalanceTest.java @@ -4,50 +4,19 @@ import io.api.etherscan.core.impl.EtherScanApi; import io.api.etherscan.error.InvalidAddressException; import io.api.etherscan.model.Balance; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - -import java.util.Arrays; -import java.util.Collection; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -@RunWith(Parameterized.class) -public class AccountBalanceTest extends ApiRunner { - - private final EtherScanApi api; - private final String addressCorrect; - private final String addressInvalid; - private final String addressNoResponse; - - public AccountBalanceTest(EtherScanApi api, String addressCorrect, String addressInvalid, String addressNoResponse) { - this.api = api; - this.addressCorrect = addressCorrect; - this.addressInvalid = addressInvalid; - this.addressNoResponse = addressNoResponse; - } +class AccountBalanceTest extends ApiRunner { - @Parameters - public static Collection data() { - return Arrays.asList(new Object[][] { - { - getApi(), - "0x8d4426f94e42f721C7116E81d6688cd935cB3b4F", - "8d4426f94e42f721C7116E81d6688cd935cB3b4F", - "0x1d4426f94e42f721C7116E81d6688cd935cB3b4F" - } - }); - } + private final EtherScanApi api = getApi(); @Test - public void correct() { - Balance balance = api.account().balance(addressCorrect); + void correct() { + Balance balance = api.account().balance("0x8d4426f94e42f721C7116E81d6688cd935cB3b4F"); assertNotNull(balance); assertNotNull(balance.getWei()); assertNotNull(balance.getMwei()); @@ -58,14 +27,14 @@ public void correct() { assertNotNull(balance.toString()); } - @Test(expected = InvalidAddressException.class) - public void invalidParamWithError() { - Balance balance = getApi().account().balance(addressInvalid); + @Test + void invalidParamWithError() { + assertThrows(InvalidAddressException.class, () -> getApi().account().balance("8d4426f94e42f721C7116E81d6688cd935cB3b4F")); } @Test - public void correctParamWithEmptyExpectedResult() { - Balance balance = api.account().balance(addressNoResponse); + void correctParamWithEmptyExpectedResult() { + Balance balance = api.account().balance("0x1d4426f94e42f721C7116E81d6688cd935cB3b4F"); assertNotNull(balance); assertNotNull(balance.getWei()); assertNotNull(balance.getAddress()); diff --git a/src/test/java/io/api/etherscan/account/AccountMinedBlocksTest.java b/src/test/java/io/api/etherscan/account/AccountMinedBlocksTest.java index 3a46858..ae16174 100644 --- a/src/test/java/io/api/etherscan/account/AccountMinedBlocksTest.java +++ b/src/test/java/io/api/etherscan/account/AccountMinedBlocksTest.java @@ -4,61 +4,23 @@ import io.api.etherscan.core.impl.EtherScanApi; import io.api.etherscan.error.InvalidAddressException; import io.api.etherscan.model.Block; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - -import java.util.Arrays; -import java.util.Collection; import java.util.List; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -@RunWith(Parameterized.class) -public class AccountMinedBlocksTest extends ApiRunner { - - private final EtherScanApi api; - private final int blocksMined; - private final String addressCorrect; - private final String addressInvalid; - private final String addressNoResponse; - - public AccountMinedBlocksTest(EtherScanApi api, - int blocksMined, - String addressCorrect, - String addressInvalid, - String addressNoResponse) { - this.api = api; - this.blocksMined = blocksMined; - this.addressCorrect = addressCorrect; - this.addressInvalid = addressInvalid; - this.addressNoResponse = addressNoResponse; - } +class AccountMinedBlocksTest extends ApiRunner { - @Parameters - public static Collection data() { - return Arrays.asList(new Object[][] { - { - getApi(), - 223, - "0xE4C6175183029A0f039bf2DFffa5C6e8F3cA9B23", - "xE4C6175183029A0f039bf2DFffa5C6e8F3cA9B23", - "0xE1C6175183029A0f039bf2DFffa5C6e8F3cA9B23", - } - }); - } + private final EtherScanApi api = getApi(); @Test - public void correct() { - List blocks = api.account().minedBlocks(addressCorrect); + void correct() { + List blocks = api.account().minedBlocks("0xE4C6175183029A0f039bf2DFffa5C6e8F3cA9B23"); assertNotNull(blocks); - assertEquals(blocksMined, blocks.size()); + assertEquals(223, blocks.size()); assertBlocks(blocks); assertNotNull(blocks.get(0).toString()); @@ -68,14 +30,15 @@ public void correct() { } } - @Test(expected = InvalidAddressException.class) - public void invalidParamWithError() { - List txs = getApi().account().minedBlocks(addressInvalid); + @Test + void invalidParamWithError() { + assertThrows(InvalidAddressException.class, + () -> getApi().account().minedBlocks("xE4C6175183029A0f039bf2DFffa5C6e8F3cA9B23")); } @Test - public void correctParamWithEmptyExpectedResult() { - List txs = api.account().minedBlocks(addressNoResponse); + void correctParamWithEmptyExpectedResult() { + List txs = api.account().minedBlocks("0xE1C6175183029A0f039bf2DFffa5C6e8F3cA9B23"); assertNotNull(txs); assertTrue(txs.isEmpty()); } diff --git a/src/test/java/io/api/etherscan/account/AccountTokenBalanceTest.java b/src/test/java/io/api/etherscan/account/AccountTokenBalanceTest.java index 2794e95..b8b8146 100644 --- a/src/test/java/io/api/etherscan/account/AccountTokenBalanceTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTokenBalanceTest.java @@ -3,63 +3,21 @@ import io.api.ApiRunner; import io.api.etherscan.core.impl.EtherScanApi; import io.api.etherscan.error.InvalidAddressException; -import io.api.etherscan.model.Balance; import io.api.etherscan.model.TokenBalance; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - -import java.util.Arrays; -import java.util.Collection; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -@RunWith(Parameterized.class) -public class AccountTokenBalanceTest extends ApiRunner { - - private final EtherScanApi api; - private final String contractValid; - private final String addressValid; - private final String contractInvalid; - private final String addressInvalid; - private final String addressEmpty; +class AccountTokenBalanceTest extends ApiRunner { - public AccountTokenBalanceTest(EtherScanApi api, - String contractValid, - String addressValid, - String contractInvalid, - String addressInvalid, - String addressEmpty) { - this.api = api; - this.contractValid = contractValid; - this.addressValid = addressValid; - this.contractInvalid = contractInvalid; - this.addressInvalid = addressInvalid; - this.addressEmpty = addressEmpty; - } - - @Parameters - public static Collection data() { - return Arrays.asList(new Object[][] { - { - getApi(), - "0x5EaC95ad5b287cF44E058dCf694419333b796123", - "0x5d807e7F124EC2103a59c5249187f772c0b8D6b2", - "0xEaC95ad5b287cF44E058dCf694419333b796123", - "0x5807e7F124EC2103a59c5249187f772c0b8D6b2", - "0x1d807e7F124EC2103a59c5249187f772c0b8D6b2", - } - }); - } + private final EtherScanApi api = getApi(); @Test - public void correct() { - TokenBalance balance = api.account().balance(addressValid, contractValid); + void correct() { + TokenBalance balance = api.account().balance("0x5d807e7F124EC2103a59c5249187f772c0b8D6b2", + "0x5EaC95ad5b287cF44E058dCf694419333b796123"); assertNotNull(balance); assertNotNull(balance.getWei()); assertNotNull(balance.getAddress()); @@ -71,19 +29,22 @@ public void correct() { assertNotEquals(balance.hashCode(), balance2.hashCode()); } - @Test(expected = InvalidAddressException.class) - public void invalidAddressParamWithError() { - Balance balance = api.account().balance(addressInvalid, contractValid); + @Test + void invalidAddressParamWithError() { + assertThrows(InvalidAddressException.class, () -> api.account().balance("0x5807e7F124EC2103a59c5249187f772c0b8D6b2", + "0x5EaC95ad5b287cF44E058dCf694419333b796123")); } - @Test(expected = InvalidAddressException.class) - public void invalidContractParamWithError() { - Balance balance = api.account().balance(addressValid, contractInvalid); + @Test + void invalidContractParamWithError() { + assertThrows(InvalidAddressException.class, () -> api.account().balance("0x5d807e7F124EC2103a59c5249187f772c0b8D6b2", + "0xEaC95ad5b287cF44E058dCf694419333b796123")); } @Test - public void correctParamWithEmptyExpectedResult() { - TokenBalance balance = api.account().balance(addressEmpty, contractValid); + void correctParamWithEmptyExpectedResult() { + TokenBalance balance = api.account().balance("0x1d807e7F124EC2103a59c5249187f772c0b8D6b2", + "0x5EaC95ad5b287cF44E058dCf694419333b796123"); assertNotNull(balance); assertNotNull(balance.getWei()); assertNotNull(balance.getAddress()); diff --git a/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java b/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java index 126fd90..4e63dbc 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java @@ -5,56 +5,23 @@ import io.api.etherscan.error.InvalidTxHashException; import io.api.etherscan.model.TxInternal; import io.api.etherscan.util.BasicUtils; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - -import java.util.Arrays; -import java.util.Collection; import java.util.List; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -@RunWith(Parameterized.class) -public class AccountTxInternalByHashTest extends ApiRunner { - - private final EtherScanApi api; - private final int txAmount; - private final String validTx; - private final String invalidTx; - private final String emptyTx; - - public AccountTxInternalByHashTest(EtherScanApi api, int txAmount, String validTx, String invalidTx, String emptyTx) { - this.api = api; - this.txAmount = txAmount; - this.validTx = validTx; - this.invalidTx = invalidTx; - this.emptyTx = emptyTx; - } +class AccountTxInternalByHashTest extends ApiRunner { - @Parameters - public static Collection data() { - return Arrays.asList(new Object[][] { - { - getApi(), - 1, - "0x1b513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b", - "0xb513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b", - "0x2b513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b", - } - }); - } + private final EtherScanApi api = getApi(); @Test - public void correct() { - List txs = api.account().txsInternalByHash(validTx); + void correct() { + List txs = api.account() + .txsInternalByHash("0x1b513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b"); assertNotNull(txs); - assertEquals(txAmount, txs.size()); + assertEquals(1, txs.size()); assertTxs(txs); assertNotNull(txs.get(0).getFrom()); assertNotNull(txs.get(0).getTimeStamp()); @@ -73,14 +40,16 @@ public void correct() { } } - @Test(expected = InvalidTxHashException.class) - public void invalidParamWithError() { - List txs = api.account().txsInternalByHash(invalidTx); + @Test + void invalidParamWithError() { + assertThrows(InvalidTxHashException.class, + () -> api.account().txsInternalByHash("0xb513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b")); } @Test - public void correctParamWithEmptyExpectedResult() { - List txs = api.account().txsInternalByHash(emptyTx); + void correctParamWithEmptyExpectedResult() { + List txs = api.account() + .txsInternalByHash("0x2b513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b"); assertNotNull(txs); assertTrue(txs.isEmpty()); } diff --git a/src/test/java/io/api/etherscan/account/AccountTxInternalTest.java b/src/test/java/io/api/etherscan/account/AccountTxInternalTest.java index 47f3e61..7144671 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxInternalTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTxInternalTest.java @@ -3,20 +3,17 @@ import io.api.ApiRunner; import io.api.etherscan.error.InvalidAddressException; import io.api.etherscan.model.TxInternal; -import org.junit.Test; - import java.util.List; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class AccountTxInternalTest extends ApiRunner { +class AccountTxInternalTest extends ApiRunner { @Test - public void correct() { + void correct() { List txs = getApi().account().txsInternal("0x2C1ba59D6F58433FB1EaEe7d20b26Ed83bDA51A3"); assertNotNull(txs); assertEquals(66, txs.size()); @@ -25,7 +22,7 @@ public void correct() { } @Test - public void correctStartBlock() { + void correctStartBlock() { List txs = getApi().account().txsInternal("0x2C1ba59D6F58433FB1EaEe7d20b26Ed83bDA51A3", 2558775); assertNotNull(txs); assertEquals(24, txs.size()); @@ -35,20 +32,21 @@ public void correctStartBlock() { } @Test - public void correctStartBlockEndBlock() { + void correctStartBlockEndBlock() { List txs = getApi().account().txsInternal("0x2C1ba59D6F58433FB1EaEe7d20b26Ed83bDA51A3", 2558775, 2685504); assertNotNull(txs); assertEquals(21, txs.size()); assertTxs(txs); } - @Test(expected = InvalidAddressException.class) - public void invalidParamWithError() { - List txs = getApi().account().txsInternal("0x2C1ba59D6F58433FB1EaEe7d20b26Ed83bDA51"); + @Test + void invalidParamWithError() { + assertThrows(InvalidAddressException.class, + () -> getApi().account().txsInternal("0x2C1ba59D6F58433FB1EaEe7d20b26Ed83bDA51")); } @Test - public void correctParamWithEmptyExpectedResult() { + void correctParamWithEmptyExpectedResult() { List txs = getApi().account().txsInternal("0x2C1ba59D6F58433FB2EaEe7d20b26Ed83bDA51A3"); assertNotNull(txs); assertTrue(txs.isEmpty()); diff --git a/src/test/java/io/api/etherscan/account/AccountTxRc721TokenTest.java b/src/test/java/io/api/etherscan/account/AccountTxRc721TokenTest.java index 0afa12f..6601d1a 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxRc721TokenTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTxRc721TokenTest.java @@ -3,18 +3,17 @@ import io.api.ApiRunner; import io.api.etherscan.error.InvalidAddressException; import io.api.etherscan.model.TxToken; -import org.junit.Test; - import java.util.List; +import org.junit.jupiter.api.Test; /** * @author NGuggs * @since 11.28.2021 */ -public class AccountTxRc721TokenTest extends ApiRunner { +class AccountTxRc721TokenTest extends ApiRunner { @Test - public void correct() { + void correct() { List txs = getApi().account().txsNftToken("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67"); assertNotNull(txs); assertEquals(16, txs.size()); @@ -33,7 +32,7 @@ public void correct() { } @Test - public void correctStartBlock() { + void correctStartBlock() { List txs = getApi().account().txsNftToken("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67", 4762071); System.out.println(txs); assertNotNull(txs); @@ -42,7 +41,7 @@ public void correctStartBlock() { } @Test - public void correctStartBlockEndBlock() { + void correctStartBlockEndBlock() { List txs = getApi().account().txsNftToken("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67", 4761862, 4761934); System.out.println(txs); assertNotNull(txs); @@ -50,13 +49,14 @@ public void correctStartBlockEndBlock() { assertTxs(txs); } - @Test(expected = InvalidAddressException.class) - public void invalidParamWithError() { - getApi().account().txsNftToken("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); + @Test + void invalidParamWithError() { + assertThrows(InvalidAddressException.class, + () -> getApi().account().txsNftToken("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7")); } @Test - public void correctParamWithEmptyExpectedResult() { + void correctParamWithEmptyExpectedResult() { List txs = getApi().account().txsNftToken("0x31ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); assertNotNull(txs); assertTrue(txs.isEmpty()); diff --git a/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java b/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java index b82d4d1..044991b 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java @@ -3,20 +3,17 @@ import io.api.ApiRunner; import io.api.etherscan.error.InvalidAddressException; import io.api.etherscan.model.TxToken; -import org.junit.Test; - import java.util.List; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class AccountTxTokenTest extends ApiRunner { +class AccountTxTokenTest extends ApiRunner { @Test - public void correct() { + void correct() { List txs = getApi().account().txsToken("0xE376F69ED2218076682e2b3B7b9099eC50aD68c4"); assertNotNull(txs); assertEquals(3, txs.size()); @@ -35,7 +32,7 @@ public void correct() { } @Test - public void correctStartBlock() { + void correctStartBlock() { List txs = getApi().account().txsToken("0x36ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7", 5578167); assertNotNull(txs); assertEquals(11, txs.size()); @@ -43,20 +40,21 @@ public void correctStartBlock() { } @Test - public void correctStartBlockEndBlock() { + void correctStartBlockEndBlock() { List txs = getApi().account().txsToken("0x36ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7", 5578167, 5813576); assertNotNull(txs); assertEquals(5, txs.size()); assertTxs(txs); } - @Test(expected = InvalidAddressException.class) - public void invalidParamWithError() { - getApi().account().txsToken("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); + @Test + void invalidParamWithError() { + assertThrows(InvalidAddressException.class, + () -> getApi().account().txsToken("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7")); } @Test - public void correctParamWithEmptyExpectedResult() { + void correctParamWithEmptyExpectedResult() { List txs = getApi().account().txsToken("0x31ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); assertNotNull(txs); assertTrue(txs.isEmpty()); diff --git a/src/test/java/io/api/etherscan/account/AccountTxsTest.java b/src/test/java/io/api/etherscan/account/AccountTxsTest.java index 66a95e4..899d0fb 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxsTest.java +++ b/src/test/java/io/api/etherscan/account/AccountTxsTest.java @@ -3,20 +3,17 @@ import io.api.ApiRunner; import io.api.etherscan.error.InvalidAddressException; import io.api.etherscan.model.Tx; -import org.junit.Test; - import java.util.List; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class AccountTxsTest extends ApiRunner { +class AccountTxsTest extends ApiRunner { @Test - public void correct() { + void correct() { List txs = getApi().account().txs("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9"); assertNotNull(txs); assertEquals(5, txs.size()); @@ -39,7 +36,7 @@ public void correct() { } @Test - public void correctStartBlock() { + void correctStartBlock() { List txs = getApi().account().txs("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9", 3892842); assertNotNull(txs); assertEquals(4, txs.size()); @@ -47,21 +44,21 @@ public void correctStartBlock() { } @Test - public void correctStartBlockEndBlock() { + void correctStartBlockEndBlock() { List txs = getApi().account().txs("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9", 3892842, 3945741); assertNotNull(txs); assertEquals(3, txs.size()); assertTxs(txs); - assertFalse(txs.get(0).equals(txs.get(1))); + assertNotEquals(txs.get(0), txs.get(1)); } - @Test(expected = InvalidAddressException.class) - public void invalidParamWithError() { - List txs = getApi().account().txs("9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9"); + @Test + void invalidParamWithError() { + assertThrows(InvalidAddressException.class, () -> getApi().account().txs("9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9")); } @Test - public void correctParamWithEmptyExpectedResult() { + void correctParamWithEmptyExpectedResult() { List txs = getApi().account().txs("0x9321cb34984c3992ec1EA0eAE98Ccf80A74f95B9"); assertNotNull(txs); assertTrue(txs.isEmpty()); diff --git a/src/test/java/io/api/etherscan/block/BlockApiTest.java b/src/test/java/io/api/etherscan/block/BlockApiTest.java index 34b9de5..cee8bb9 100644 --- a/src/test/java/io/api/etherscan/block/BlockApiTest.java +++ b/src/test/java/io/api/etherscan/block/BlockApiTest.java @@ -2,20 +2,17 @@ import io.api.ApiRunner; import io.api.etherscan.model.UncleBlock; -import org.junit.Test; - import java.util.Optional; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class BlockApiTest extends ApiRunner { +class BlockApiTest extends ApiRunner { @Test - public void correct() { + void correct() { Optional uncle = getApi().block().uncles(2165403); assertTrue(uncle.isPresent()); assertFalse(uncle.get().isEmpty()); @@ -46,14 +43,14 @@ public void correct() { } @Test - public void correctNoUncles() { + void correctNoUncles() { Optional uncles = getApi().block().uncles(34); assertTrue(uncles.isPresent()); assertTrue(uncles.get().getUncles().isEmpty()); } @Test - public void correctParamWithEmptyExpectedResult() { + void correctParamWithEmptyExpectedResult() { Optional uncles = getApi().block().uncles(99999999934L); assertFalse(uncles.isPresent()); } diff --git a/src/test/java/io/api/etherscan/contract/ContractApiTest.java b/src/test/java/io/api/etherscan/contract/ContractApiTest.java index 6b4d7d8..85fb905 100644 --- a/src/test/java/io/api/etherscan/contract/ContractApiTest.java +++ b/src/test/java/io/api/etherscan/contract/ContractApiTest.java @@ -3,18 +3,16 @@ import io.api.ApiRunner; import io.api.etherscan.error.InvalidAddressException; import io.api.etherscan.model.Abi; -import org.junit.Test; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class ContractApiTest extends ApiRunner { +class ContractApiTest extends ApiRunner { @Test - public void correct() { + void correct() { Abi abi = getApi().contract().contractAbi("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413"); assertNotNull(abi); assertTrue(abi.isVerified()); @@ -27,13 +25,14 @@ public void correct() { assertNotEquals(empty.hashCode(), abi.hashCode()); } - @Test(expected = InvalidAddressException.class) - public void invalidParamWithError() { - getApi().contract().contractAbi("0xBBbc244D798123fDe783fCc1C72d3Bb8C189413"); + @Test + void invalidParamWithError() { + assertThrows(InvalidAddressException.class, + () -> getApi().contract().contractAbi("0xBBbc244D798123fDe783fCc1C72d3Bb8C189413")); } @Test - public void correctParamWithEmptyExpectedResult() { + void correctParamWithEmptyExpectedResult() { Abi abi = getApi().contract().contractAbi("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413"); assertNotNull(abi); assertTrue(abi.isVerified()); diff --git a/src/test/java/io/api/etherscan/logs/LogQueryBuilderTest.java b/src/test/java/io/api/etherscan/logs/LogQueryBuilderTest.java index 85b35e8..f956364 100644 --- a/src/test/java/io/api/etherscan/logs/LogQueryBuilderTest.java +++ b/src/test/java/io/api/etherscan/logs/LogQueryBuilderTest.java @@ -7,18 +7,16 @@ import io.api.etherscan.model.query.impl.LogQuery; import io.api.etherscan.model.query.impl.LogQueryBuilder; import io.api.etherscan.model.query.impl.LogTopicQuadro; -import org.junit.Test; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class LogQueryBuilderTest extends ApiRunner { +class LogQueryBuilderTest extends ApiRunner { @Test - public void singleCorrect() { + void singleCorrect() { LogQuery single = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") .build(); @@ -27,28 +25,22 @@ public void singleCorrect() { assertNotNull(single.getParams()); } - @Test(expected = InvalidAddressException.class) - public void singleInCorrectAddress() { - LogQuery single = LogQueryBuilder.with("033990122638b9132ca29c723bdf037f1a891a70c") + @Test + void singleInCorrectAddress() { + assertThrows(InvalidAddressException.class, () -> LogQueryBuilder.with("033990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") - .build(); - - assertNotNull(single); - assertNotNull(single.getParams()); + .build()); } - @Test(expected = LogQueryException.class) - public void singleInCorrectTopic() { - LogQuery single = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") + @Test + void singleInCorrectTopic() { + assertThrows(LogQueryException.class, () -> LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("6516=") - .build(); - - assertNotNull(single); - assertNotNull(single.getParams()); + .build()); } @Test - public void tupleCorrect() { + void tupleCorrect() { LogQuery tuple = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224) .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000") @@ -59,20 +51,17 @@ public void tupleCorrect() { assertNotNull(tuple.getParams()); } - @Test(expected = LogQueryException.class) - public void tupleInCorrectOp() { - LogQuery tuple = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224) + @Test + void tupleInCorrectOp() { + assertThrows(LogQueryException.class, () -> LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224) .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000") .setOpTopic0_1(null) - .build(); - - assertNotNull(tuple); - assertNotNull(tuple.getParams()); + .build()); } @Test - public void tripleCorrect() { + void tripleCorrect() { LogQuery triple = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", @@ -86,68 +75,60 @@ public void tripleCorrect() { assertNotNull(triple.getParams()); } - @Test(expected = LogQueryException.class) - public void tripleInCorrectOp() { - LogQuery triple = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - "0x72657075746174696f6e00000000000000000000000000000000000000000000", - "0x72657075746174696f6e00000000000000000000000000000000000000000000") - .setOpTopic0_1(LogOp.AND) - .setOpTopic0_2(null) - .setOpTopic1_2(LogOp.AND) - .build(); - - assertNotNull(triple); - assertNotNull(triple.getParams()); + @Test + void tripleInCorrectOp() { + assertThrows(LogQueryException.class, + () -> LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) + .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0x72657075746174696f6e00000000000000000000000000000000000000000000", + "0x72657075746174696f6e00000000000000000000000000000000000000000000") + .setOpTopic0_1(LogOp.AND) + .setOpTopic0_2(null) + .setOpTopic1_2(LogOp.AND) + .build()); } - @Test(expected = LogQueryException.class) - public void tripleInCorrectTopic1() { - LogQuery triple = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) - .topic(null, - "0x72657075746174696f6e00000000000000000000000000000000000000000000", - "0x72657075746174696f6e00000000000000000000000000000000000000000000") - .setOpTopic0_1(LogOp.AND) - .setOpTopic0_2(LogOp.AND) - .setOpTopic1_2(LogOp.AND) - .build(); - - assertNotNull(triple); - assertNotNull(triple.getParams()); + @Test + void tripleInCorrectTopic1() { + assertThrows(LogQueryException.class, + () -> LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) + .topic(null, + "0x72657075746174696f6e00000000000000000000000000000000000000000000", + "0x72657075746174696f6e00000000000000000000000000000000000000000000") + .setOpTopic0_1(LogOp.AND) + .setOpTopic0_2(LogOp.AND) + .setOpTopic1_2(LogOp.AND) + .build()); } - @Test(expected = LogQueryException.class) - public void tripleInCorrectTopic2() { - LogQuery triple = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - null, - "0x72657075746174696f6e00000000000000000000000000000000000000000000") - .setOpTopic0_1(LogOp.AND) - .setOpTopic0_2(LogOp.AND) - .setOpTopic1_2(LogOp.AND) - .build(); - - assertNotNull(triple); - assertNotNull(triple.getParams()); + @Test + void tripleInCorrectTopic2() { + assertThrows(LogQueryException.class, + () -> LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) + .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + null, + "0x72657075746174696f6e00000000000000000000000000000000000000000000") + .setOpTopic0_1(LogOp.AND) + .setOpTopic0_2(LogOp.AND) + .setOpTopic1_2(LogOp.AND) + .build()); } - @Test(expected = LogQueryException.class) - public void tripleInCorrectTopic3() { - LogQuery triple = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - "0x72657075746174696f6e00000000000000000000000000000000000000000000", - null) - .setOpTopic0_1(LogOp.AND) - .setOpTopic0_2(LogOp.AND) - .setOpTopic1_2(LogOp.AND) - .build(); - - assertNotNull(triple); - assertNotNull(triple.getParams()); + @Test + void tripleInCorrectTopic3() { + assertThrows(LogQueryException.class, + () -> LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) + .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0x72657075746174696f6e00000000000000000000000000000000000000000000", + null) + .setOpTopic0_1(LogOp.AND) + .setOpTopic0_2(LogOp.AND) + .setOpTopic1_2(LogOp.AND) + .build()); } @Test - public void quadroCorrect() { + void quadroCorrect() { LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", @@ -165,9 +146,9 @@ public void quadroCorrect() { assertNotNull(quadro.getParams()); } - @Test(expected = LogQueryException.class) - public void quadroIncorrectTopic2() { - LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") + @Test + void quadroIncorrectTopic2() { + assertThrows(LogQueryException.class, () -> LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", null, "0x72657075746174696f6e00000000000000000000000000000000000000000000", @@ -178,92 +159,83 @@ public void quadroIncorrectTopic2() { .setOpTopic1_2(LogOp.OR) .setOpTopic1_3(LogOp.OR) .setOpTopic2_3(LogOp.OR) - .build(); - - assertNotNull(quadro); - assertNotNull(quadro.getParams()); + .build()); } - @Test(expected = LogQueryException.class) - public void tupleIncorrectTopic2() { - LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") + @Test + void tupleIncorrectTopic2() { + assertThrows(LogQueryException.class, () -> LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", null) .setOpTopic0_1(LogOp.AND) - .build(); - - assertNotNull(quadro); - assertNotNull(quadro.getParams()); + .build()); } - @Test(expected = LogQueryException.class) - public void tupleIncorrectTopic1() { - LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") + @Test + void tupleIncorrectTopic1() { + assertThrows(LogQueryException.class, () -> LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic(null, "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") .setOpTopic0_1(LogOp.AND) - .build(); - - assertNotNull(quadro); - assertNotNull(quadro.getParams()); + .build()); } - @Test(expected = LogQueryException.class) - public void quadroIncorrectOp1() { - LogTopicQuadro topicQuadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") + @Test + void quadroIncorrectOp1() { + final LogTopicQuadro topicQuadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "0x72657075746174696f6e00000000000000000000000000000000000000000000"); - topicQuadro + assertThrows(LogQueryException.class, () -> topicQuadro .setOpTopic0_1(null) .setOpTopic0_2(LogOp.OR) .setOpTopic0_3(LogOp.AND) .setOpTopic1_2(LogOp.OR) .setOpTopic1_3(LogOp.OR) .setOpTopic2_3(LogOp.OR) - .build(); + .build()); } - @Test(expected = LogQueryException.class) - public void quadroIncorrectOp2() { - LogTopicQuadro topicQuadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") + @Test + void quadroIncorrectOp2() { + final LogTopicQuadro topicQuadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "0x72657075746174696f6e00000000000000000000000000000000000000000000"); - topicQuadro.setOpTopic0_1(LogOp.AND) + assertThrows(LogQueryException.class, () -> topicQuadro.setOpTopic0_1(LogOp.AND) .setOpTopic0_2(null) .setOpTopic0_3(LogOp.AND) .setOpTopic1_2(LogOp.OR) .setOpTopic1_3(LogOp.OR) .setOpTopic2_3(LogOp.OR) - .build(); + .build()); } - @Test(expected = LogQueryException.class) - public void quadroIncorrectOp3() { - LogTopicQuadro topicQuadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") + @Test + void quadroIncorrectOp3() { + final LogTopicQuadro topicQuadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "0x72657075746174696f6e00000000000000000000000000000000000000000000"); - topicQuadro + assertThrows(LogQueryException.class, () -> topicQuadro .setOpTopic0_1(LogOp.AND) .setOpTopic0_2(LogOp.OR) .setOpTopic0_3(null) .setOpTopic1_2(LogOp.OR) .setOpTopic1_3(LogOp.OR) .setOpTopic2_3(LogOp.OR) - .build(); + .build()); } - @Test(expected = LogQueryException.class) - public void quadroInCorrectAgainTopic() { - LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") + @Test + void quadroInCorrectAgainTopic() { + assertThrows(LogQueryException.class, () -> LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "0x72657075746174696f6e00000000000000000000000000000000000000000000", @@ -274,69 +246,66 @@ public void quadroInCorrectAgainTopic() { .setOpTopic1_2(LogOp.OR) .setOpTopic1_3(LogOp.OR) .setOpTopic2_3(LogOp.OR) - .build(); - - assertNotNull(quadro); - assertNotNull(quadro.getParams()); + .build()); } - @Test(expected = LogQueryException.class) - public void quadroInCorrectOp4() { - LogTopicQuadro topicQuadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") + @Test + void quadroInCorrectOp4() { + final LogTopicQuadro topicQuadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545"); - topicQuadro + assertThrows(LogQueryException.class, () -> topicQuadro .setOpTopic0_1(LogOp.AND) .setOpTopic0_2(LogOp.OR) .setOpTopic0_3(LogOp.AND) .setOpTopic1_2(null) .setOpTopic1_3(LogOp.OR) .setOpTopic2_3(LogOp.OR) - .build(); + .build()); } - @Test(expected = LogQueryException.class) - public void quadroInCorrectOp5() { - LogTopicQuadro topicQuadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") + @Test + void quadroInCorrectOp5() { + final LogTopicQuadro topicQuadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545"); - topicQuadro + assertThrows(LogQueryException.class, () -> topicQuadro .setOpTopic0_1(LogOp.AND) .setOpTopic0_2(LogOp.OR) .setOpTopic0_3(LogOp.AND) .setOpTopic1_2(LogOp.AND) .setOpTopic1_3(null) .setOpTopic2_3(LogOp.OR) - .build(); + .build()); } - @Test(expected = LogQueryException.class) - public void quadroInCorrectOp6() { + @Test + void quadroInCorrectOp6() { LogTopicQuadro topicQuadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545"); - topicQuadro + assertThrows(LogQueryException.class, () -> topicQuadro .setOpTopic0_1(LogOp.AND) .setOpTopic0_2(LogOp.OR) .setOpTopic0_3(LogOp.AND) .setOpTopic1_2(LogOp.AND) .setOpTopic1_3(LogOp.OR) .setOpTopic2_3(null) - .build(); + .build()); } - @Test(expected = LogQueryException.class) - public void quadroInCorrectTopic() { - LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") + @Test + void quadroInCorrectTopic() { + assertThrows(LogQueryException.class, () -> LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "", @@ -347,9 +316,6 @@ public void quadroInCorrectTopic() { .setOpTopic1_2(LogOp.OR) .setOpTopic1_3(LogOp.OR) .setOpTopic2_3(LogOp.OR) - .build(); - - assertNotNull(quadro); - assertNotNull(quadro.getParams()); + .build()); } } diff --git a/src/test/java/io/api/etherscan/logs/LogsApiTest.java b/src/test/java/io/api/etherscan/logs/LogsApiTest.java index 7143a83..e786e0c 100644 --- a/src/test/java/io/api/etherscan/logs/LogsApiTest.java +++ b/src/test/java/io/api/etherscan/logs/LogsApiTest.java @@ -5,34 +5,19 @@ import io.api.etherscan.model.query.LogOp; import io.api.etherscan.model.query.impl.LogQuery; import io.api.etherscan.model.query.impl.LogQueryBuilder; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; -import org.junit.runners.Parameterized.Parameters; - -import java.util.Arrays; -import java.util.Collection; import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -@RunWith(Parameterized.class) -public class LogsApiTest extends ApiRunner { - - private final LogQuery query; - private final int logsSize; - - public LogsApiTest(LogQuery query, int logsSize) { - this.query = query; - this.logsSize = logsSize; - } +class LogsApiTest extends ApiRunner { - @Parameters - public static Collection data() { + static Stream source() { LogQuery single = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") .build(); @@ -53,16 +38,16 @@ public static Collection data() { .setOpTopic0_1(LogOp.OR) .build(); - return Arrays.asList(new Object[][] { - { single, 423 }, - { singleInvalidAddr, 0 }, - { tupleAnd, 1 }, - { tupleOr, 425 } - }); + return Stream.of( + Arguments.of(single, 423), + Arguments.of(singleInvalidAddr, 0), + Arguments.of(tupleAnd, 1), + Arguments.of(tupleOr, 425)); } - @Test - public void validateQuery() { + @ParameterizedTest + @MethodSource("source") + void validateQuery(LogQuery query, int logsSize) { List logs = getApi().logs().logs(query); assertEquals(logsSize, logs.size()); diff --git a/src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java index 5d3884d..faead19 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java @@ -5,27 +5,24 @@ import io.api.etherscan.manager.impl.QueueManager; import io.api.etherscan.model.EthNetwork; import io.api.etherscan.model.proxy.BlockProxy; -import org.junit.Test; - import java.util.Optional; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class ProxyBlockApiTest extends ApiRunner { +class ProxyBlockApiTest extends ApiRunner { private final EtherScanApi api; - public ProxyBlockApiTest() { + ProxyBlockApiTest() { final QueueManager queueManager = new QueueManager(1, 5100L, 5100L, 0); this.api = new EtherScanApi(getApiKey(), EthNetwork.MAINNET, queueManager); } @Test - public void correct() { + void correct() { Optional block = api.proxy().block(5120); assertTrue(block.isPresent()); BlockProxy proxy = block.get(); @@ -58,13 +55,13 @@ public void correct() { } @Test - public void correctParamWithEmptyExpectedResult() { + void correctParamWithEmptyExpectedResult() { Optional block = api.proxy().block(99999999999L); assertFalse(block.isPresent()); } @Test - public void correctParamNegativeNo() { + void correctParamNegativeNo() { Optional block = api.proxy().block(-1); assertTrue(block.isPresent()); assertNotNull(block.get().getHash()); diff --git a/src/test/java/io/api/etherscan/proxy/ProxyBlockLastNoApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyBlockLastNoApiTest.java index 5485391..f866b6a 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyBlockLastNoApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyBlockLastNoApiTest.java @@ -1,18 +1,16 @@ package io.api.etherscan.proxy; import io.api.ApiRunner; -import org.junit.Test; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 13.11.2018 */ -public class ProxyBlockLastNoApiTest extends ApiRunner { +class ProxyBlockLastNoApiTest extends ApiRunner { @Test - public void correct() { + void correct() { long noLast = getApi().proxy().blockNoLast(); assertNotEquals(0, noLast); } diff --git a/src/test/java/io/api/etherscan/proxy/ProxyBlockUncleApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyBlockUncleApiTest.java index 474c5bb..67a8875 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyBlockUncleApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyBlockUncleApiTest.java @@ -2,20 +2,17 @@ import io.api.ApiRunner; import io.api.etherscan.model.proxy.BlockProxy; -import org.junit.Test; - import java.util.Optional; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 13.11.2018 */ -public class ProxyBlockUncleApiTest extends ApiRunner { +class ProxyBlockUncleApiTest extends ApiRunner { @Test - public void correct() { + void correct() { Optional block = getApi().proxy().blockUncle(603183, 0); assertTrue(block.isPresent()); assertNotNull(block.get().getHash()); @@ -23,13 +20,13 @@ public void correct() { } @Test - public void correctParamWithEmptyExpectedResult() { + void correctParamWithEmptyExpectedResult() { Optional block = getApi().proxy().blockUncle(5120, 1); assertFalse(block.isPresent()); } @Test - public void correctParamNegativeNo() { + void correctParamNegativeNo() { Optional block = getApi().proxy().blockUncle(-603183, 0); assertFalse(block.isPresent()); } diff --git a/src/test/java/io/api/etherscan/proxy/ProxyCallApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyCallApiTest.java index 07d26bd..8cf46c9 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyCallApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyCallApiTest.java @@ -4,43 +4,40 @@ import io.api.etherscan.error.InvalidAddressException; import io.api.etherscan.error.InvalidDataHexException; import io.api.etherscan.util.BasicUtils; -import org.junit.Test; - import java.util.Optional; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class ProxyCallApiTest extends ApiRunner { +class ProxyCallApiTest extends ApiRunner { @Test - public void correct() { + void correct() { Optional call = getApi().proxy().call("0xAEEF46DB4855E25702F8237E8f403FddcaF931C0", "0x70a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724"); assertTrue(call.isPresent()); assertFalse(BasicUtils.isNotHex(call.get())); } - @Test(expected = InvalidAddressException.class) - public void invalidParamWithError() { - Optional call = getApi().proxy().call("0xEEF46DB4855E25702F8237E8f403FddcaF931C0", - "0x70a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724"); + @Test + void invalidParamWithError() { + assertThrows(InvalidAddressException.class, () -> getApi().proxy().call("0xEEF46DB4855E25702F8237E8f403FddcaF931C0", + "0x70a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724")); } - @Test(expected = InvalidDataHexException.class) - public void invalidParamNotHex() { - Optional call = getApi().proxy().call("0xAEEF46DB4855E25702F8237E8f403FddcaF931C0", - "7-0a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724"); + @Test + void invalidParamNotHex() { + assertThrows(InvalidDataHexException.class, () -> getApi().proxy().call("0xAEEF46DB4855E25702F8237E8f403FddcaF931C0", + "7-0a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724")); } @Test - public void correctParamWithEmptyExpectedResult() { + void correctParamWithEmptyExpectedResult() { Optional call = getApi().proxy().call("0xAEEF16DB4855E25702F8237E8f403FddcaF931C0", "0x70a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724"); assertTrue(call.isPresent()); - assertFalse(call.get(), BasicUtils.isNotHex(call.get())); + assertFalse(BasicUtils.isNotHex(call.get()), 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 9e4910c..6835f07 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyCodeApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyCodeApiTest.java @@ -3,34 +3,31 @@ import io.api.ApiRunner; import io.api.etherscan.error.InvalidAddressException; import io.api.etherscan.util.BasicUtils; -import org.junit.Test; - import java.util.Optional; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class ProxyCodeApiTest extends ApiRunner { +class ProxyCodeApiTest extends ApiRunner { @Test - public void correct() { + void correct() { Optional call = getApi().proxy().code("0xf75e354c5edc8efed9b59ee9f67a80845ade7d0c"); assertTrue(call.isPresent()); - assertFalse(call.get(), BasicUtils.isNotHex(call.get())); + assertFalse(BasicUtils.isNotHex(call.get()), call.get()); } - @Test(expected = InvalidAddressException.class) - public void invalidParamWithError() { - getApi().proxy().code("0f75e354c5edc8efed9b59ee9f67a80845ade7d0c"); + @Test + void invalidParamWithError() { + assertThrows(InvalidAddressException.class, () -> getApi().proxy().code("0f75e354c5edc8efed9b59ee9f67a80845ade7d0c")); } @Test - public void correctParamWithEmptyExpectedResult() { + void correctParamWithEmptyExpectedResult() { Optional call = getApi().proxy().code("0xf15e354c5edc8efed9b59ee9f67a80845ade7d0c"); assertTrue(call.isPresent()); - assertFalse(call.get(), BasicUtils.isNotHex(call.get())); + assertFalse(BasicUtils.isNotHex(call.get()), call.get()); } } diff --git a/src/test/java/io/api/etherscan/proxy/ProxyGasApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyGasApiTest.java index 63e476c..b4b6f37 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyGasApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyGasApiTest.java @@ -2,34 +2,31 @@ import io.api.ApiRunner; import io.api.etherscan.error.InvalidDataHexException; -import org.junit.Test; - import java.math.BigInteger; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class ProxyGasApiTest extends ApiRunner { +class ProxyGasApiTest extends ApiRunner { @Test - public void correctPrice() { + void correctPrice() { BigInteger price = getApi().proxy().gasPrice(); assertNotNull(price); assertNotEquals(0, price.intValue()); } @Test - public void correctEstimated() { + void correctEstimated() { BigInteger price = getApi().proxy().gasEstimated(); assertNotNull(price); assertNotEquals(0, price.intValue()); } @Test - public void correctEstimatedWithData() { + void correctEstimatedWithData() { String dataCustom = "606060405260728060106000396000f360606040526000606060405260728060106000396000f360606040526000"; BigInteger price = getApi().proxy().gasEstimated(); BigInteger priceCustom = getApi().proxy().gasEstimated(dataCustom); @@ -38,9 +35,9 @@ public void correctEstimatedWithData() { assertNotEquals(price, priceCustom); } - @Test(expected = InvalidDataHexException.class) - public void invalidParamWithError() { + @Test + void invalidParamWithError() { String dataCustom = "280&60106000396000f360606040526000"; - BigInteger priceCustom = getApi().proxy().gasEstimated(dataCustom); + assertThrows(InvalidDataHexException.class, () -> getApi().proxy().gasEstimated(dataCustom)); } } diff --git a/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java index 19945e2..6d2e8e4 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java @@ -2,31 +2,29 @@ import io.api.ApiRunner; import io.api.etherscan.error.InvalidAddressException; -import org.junit.Test; - import java.util.Optional; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class ProxyStorageApiTest extends ApiRunner { +class ProxyStorageApiTest extends ApiRunner { @Test - public void correct() { + void correct() { Optional call = getApi().proxy().storageAt("0x6e03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 0); assertFalse(call.isPresent()); } - @Test(expected = InvalidAddressException.class) - public void invalidParamWithError() { - getApi().proxy().storageAt("0xe03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 0); + @Test + void invalidParamWithError() { + assertThrows(InvalidAddressException.class, + () -> getApi().proxy().storageAt("0xe03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 0)); } @Test - public void correctParamWithEmptyExpectedResult() { + void correctParamWithEmptyExpectedResult() { final Optional call = getApi().proxy().storageAt("0x6e03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 10000); assertFalse(call.isPresent()); } diff --git a/src/test/java/io/api/etherscan/proxy/ProxyTxApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyTxApiTest.java index 2779120..decf95f 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyTxApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyTxApiTest.java @@ -3,20 +3,17 @@ import io.api.ApiRunner; import io.api.etherscan.error.InvalidTxHashException; import io.api.etherscan.model.proxy.TxProxy; -import org.junit.Test; - import java.util.Optional; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class ProxyTxApiTest extends ApiRunner { +class ProxyTxApiTest extends ApiRunner { @Test - public void correctByHash() { + void correctByHash() { Optional tx = getApi().proxy().tx("0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); assertTrue(tx.isPresent()); assertNotNull(tx.get().getBlockHash()); @@ -33,7 +30,7 @@ public void correctByHash() { } @Test - public void correctByBlockNo() { + void correctByBlockNo() { Optional tx = getApi().proxy().tx(637368, 0); assertTrue(tx.isPresent()); assertNotNull(tx.get().getBlockHash()); @@ -52,19 +49,20 @@ public void correctByBlockNo() { assertNotNull(tx.get().getInput()); } - @Test(expected = InvalidTxHashException.class) - public void invalidParamWithError() { - Optional tx = getApi().proxy().tx("0xe2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); + @Test + void invalidParamWithError() { + assertThrows(InvalidTxHashException.class, + () -> getApi().proxy().tx("0xe2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1")); } @Test - public void correctParamWithEmptyExpectedResultBlockNoExist() { + void correctParamWithEmptyExpectedResultBlockNoExist() { Optional tx = getApi().proxy().tx(99999999L, 0); assertFalse(tx.isPresent()); } @Test - public void correctParamWithEmptyExpectedResult() { + void correctParamWithEmptyExpectedResult() { Optional tx = getApi().proxy().tx("0x2e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); assertFalse(tx.isPresent()); } diff --git a/src/test/java/io/api/etherscan/proxy/ProxyTxCountApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyTxCountApiTest.java index b81926f..0083f7a 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyTxCountApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyTxCountApiTest.java @@ -2,41 +2,40 @@ import io.api.ApiRunner; import io.api.etherscan.error.InvalidAddressException; -import org.junit.Test; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class ProxyTxCountApiTest extends ApiRunner { +class ProxyTxCountApiTest extends ApiRunner { @Test - public void correctSended() { + void correctSended() { int count = getApi().proxy().txSendCount("0x2910543af39aba0cd09dbb2d50200b3e800a63d2"); assertNotEquals(0, count); } @Test - public void correctByBlockNo() { + void correctByBlockNo() { int count = getApi().proxy().txCount(6137420); assertNotEquals(0, count); } - @Test(expected = InvalidAddressException.class) - public void invalidParamWithError() { - int count = getApi().proxy().txSendCount("0xe03d9cce9d60f3e9f2597e13cd4c54c55330cfd"); + @Test + void invalidParamWithError() { + assertThrows(InvalidAddressException.class, + () -> getApi().proxy().txSendCount("0xe03d9cce9d60f3e9f2597e13cd4c54c55330cfd")); } @Test - public void correctParamWithEmptyExpectedResultBlockNoExist() { + void correctParamWithEmptyExpectedResultBlockNoExist() { int count = getApi().proxy().txCount(99999999999L); assertNotEquals(1, count); } @Test - public void correctParamWithEmptyExpectedResult() { + void correctParamWithEmptyExpectedResult() { int count = getApi().proxy().txSendCount("0x1e03d9cce9d60f3e9f2597e13cd4c54c55330cfd"); assertNotEquals(1, count); } diff --git a/src/test/java/io/api/etherscan/proxy/ProxyTxReceiptApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyTxReceiptApiTest.java index c4a3383..0159ed9 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyTxReceiptApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyTxReceiptApiTest.java @@ -3,20 +3,17 @@ import io.api.ApiRunner; import io.api.etherscan.error.InvalidTxHashException; import io.api.etherscan.model.proxy.ReceiptProxy; -import org.junit.Test; - import java.util.Optional; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class ProxyTxReceiptApiTest extends ApiRunner { +class ProxyTxReceiptApiTest extends ApiRunner { @Test - public void correct() { + void correct() { Optional infoProxy = getApi().proxy() .txReceipt("0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); assertTrue(infoProxy.isPresent()); @@ -40,14 +37,14 @@ public void correct() { assertNotEquals(empty.hashCode(), infoProxy.get().hashCode()); } - @Test(expected = InvalidTxHashException.class) - public void invalidParamWithError() { - Optional infoProxy = getApi().proxy() - .txReceipt("0xe2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); + @Test + void invalidParamWithError() { + assertThrows(InvalidTxHashException.class, () -> getApi().proxy() + .txReceipt("0xe2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1")); } @Test - public void correctParamWithEmptyExpectedResult() { + void correctParamWithEmptyExpectedResult() { Optional infoProxy = getApi().proxy() .txReceipt("0x2e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); assertFalse(infoProxy.isPresent()); diff --git a/src/test/java/io/api/etherscan/proxy/ProxyTxSendRawApiTest.java b/src/test/java/io/api/etherscan/proxy/ProxyTxSendRawApiTest.java index 40e79a6..676dc3a 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyTxSendRawApiTest.java +++ b/src/test/java/io/api/etherscan/proxy/ProxyTxSendRawApiTest.java @@ -3,36 +3,33 @@ import io.api.ApiRunner; import io.api.etherscan.error.EtherScanException; import io.api.etherscan.error.InvalidDataHexException; -import org.junit.Test; - import java.util.Optional; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ // TODO contact etherscan and ask about method behavior -public class ProxyTxSendRawApiTest extends ApiRunner { +class ProxyTxSendRawApiTest extends ApiRunner { - public void correct() { + void correct() { Optional sendRaw = getApi().proxy() .txSendRaw("0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); assertTrue(sendRaw.isPresent()); } - @Test(expected = InvalidDataHexException.class) - public void invalidParamWithError() { - Optional sendRaw = getApi().proxy().txSendRaw("5151=0561"); + @Test + void invalidParamWithError() { + assertThrows(InvalidDataHexException.class, () -> getApi().proxy().txSendRaw("5151=0561")); } - @Test(expected = EtherScanException.class) - public void invalidParamEtherScanDataException() { - Optional sendRaw = getApi().proxy().txSendRaw("0x1"); + @Test + void invalidParamEtherScanDataException() { + assertThrows(EtherScanException.class, () -> getApi().proxy().txSendRaw("0x1")); } - public void correctParamWithEmptyExpectedResult() { + void correctParamWithEmptyExpectedResult() { Optional sendRaw = getApi().proxy().txSendRaw("0x000000"); assertFalse(sendRaw.isPresent()); } diff --git a/src/test/java/io/api/etherscan/statistic/StatisticPriceApiTest.java b/src/test/java/io/api/etherscan/statistic/StatisticPriceApiTest.java index e29a6b1..9f89738 100644 --- a/src/test/java/io/api/etherscan/statistic/StatisticPriceApiTest.java +++ b/src/test/java/io/api/etherscan/statistic/StatisticPriceApiTest.java @@ -2,18 +2,16 @@ import io.api.ApiRunner; import io.api.etherscan.model.Price; -import org.junit.Test; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class StatisticPriceApiTest extends ApiRunner { +class StatisticPriceApiTest extends ApiRunner { @Test - public void correct() { + void correct() { Price price = getApi().stats().lastPrice(); assertNotNull(price); assertNotNull(price.btcTimestamp()); diff --git a/src/test/java/io/api/etherscan/statistic/StatisticSupplyApiTest.java b/src/test/java/io/api/etherscan/statistic/StatisticSupplyApiTest.java index a705a31..32c3018 100644 --- a/src/test/java/io/api/etherscan/statistic/StatisticSupplyApiTest.java +++ b/src/test/java/io/api/etherscan/statistic/StatisticSupplyApiTest.java @@ -2,20 +2,17 @@ import io.api.ApiRunner; import io.api.etherscan.model.Supply; -import org.junit.Test; - import java.math.BigInteger; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class StatisticSupplyApiTest extends ApiRunner { +class StatisticSupplyApiTest extends ApiRunner { @Test - public void correct() { + void correct() { Supply supply = getApi().stats().supply(); assertNotNull(supply); assertNotNull(supply.getValue()); diff --git a/src/test/java/io/api/etherscan/statistic/StatisticTokenSupplyApiTest.java b/src/test/java/io/api/etherscan/statistic/StatisticTokenSupplyApiTest.java index 0a84d01..aefb2bd 100644 --- a/src/test/java/io/api/etherscan/statistic/StatisticTokenSupplyApiTest.java +++ b/src/test/java/io/api/etherscan/statistic/StatisticTokenSupplyApiTest.java @@ -2,32 +2,29 @@ import io.api.ApiRunner; import io.api.etherscan.error.InvalidAddressException; -import org.junit.Test; - import java.math.BigInteger; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class StatisticTokenSupplyApiTest extends ApiRunner { +class StatisticTokenSupplyApiTest extends ApiRunner { @Test - public void correct() { + void correct() { BigInteger supply = getApi().stats().supply("0x57d90b64a1a57749b0f932f1a3395792e12e7055"); assertNotNull(supply); assertNotEquals(BigInteger.ZERO, supply); } - @Test(expected = InvalidAddressException.class) - public void invalidParamWithError() { - BigInteger supply = getApi().stats().supply("0x7d90b64a1a57749b0f932f1a3395792e12e7055"); + @Test + void invalidParamWithError() { + assertThrows(InvalidAddressException.class, () -> getApi().stats().supply("0x7d90b64a1a57749b0f932f1a3395792e12e7055")); } @Test - public void correctParamWithEmptyExpectedResult() { + void correctParamWithEmptyExpectedResult() { BigInteger supply = getApi().stats().supply("0x51d90b64a1a57749b0f932f1a3395792e12e7055"); assertNotNull(supply); assertEquals(0, supply.intValue()); diff --git a/src/test/java/io/api/etherscan/transaction/TransactionExecApiTest.java b/src/test/java/io/api/etherscan/transaction/TransactionExecApiTest.java index 25320cc..de67a02 100644 --- a/src/test/java/io/api/etherscan/transaction/TransactionExecApiTest.java +++ b/src/test/java/io/api/etherscan/transaction/TransactionExecApiTest.java @@ -3,20 +3,17 @@ import io.api.ApiRunner; import io.api.etherscan.error.InvalidTxHashException; import io.api.etherscan.model.Status; -import org.junit.Test; - import java.util.Optional; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class TransactionExecApiTest extends ApiRunner { +class TransactionExecApiTest extends ApiRunner { @Test - public void correct() { + void correct() { Optional status = getApi().txs().execStatus("0x15f8e5ea1079d9a0bb04a4c58ae5fe7654b5b2b4463375ff7ffb490aa0032f3a"); assertTrue(status.isPresent()); assertTrue(status.get().haveError()); @@ -28,13 +25,14 @@ public void correct() { assertNotEquals(empty.hashCode(), status.get().hashCode()); } - @Test(expected = InvalidTxHashException.class) - public void invalidParamWithError() { - getApi().txs().execStatus("0xb513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b"); + @Test + void invalidParamWithError() { + assertThrows(InvalidTxHashException.class, + () -> getApi().txs().execStatus("0xb513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b")); } @Test - public void correctParamWithEmptyExpectedResult() { + void correctParamWithEmptyExpectedResult() { Optional status = getApi().txs().execStatus("0x55f8e5ea1079d9a0bb04a4c58ae5fe7654b5b2b4463375ff7ffb490aa0032f3a"); assertTrue(status.isPresent()); assertFalse(status.get().haveError()); diff --git a/src/test/java/io/api/etherscan/transaction/TransactionReceiptApiTest.java b/src/test/java/io/api/etherscan/transaction/TransactionReceiptApiTest.java index a459355..94b93b3 100644 --- a/src/test/java/io/api/etherscan/transaction/TransactionReceiptApiTest.java +++ b/src/test/java/io/api/etherscan/transaction/TransactionReceiptApiTest.java @@ -2,33 +2,31 @@ import io.api.ApiRunner; import io.api.etherscan.error.InvalidTxHashException; -import org.junit.Test; - import java.util.Optional; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class TransactionReceiptApiTest extends ApiRunner { +class TransactionReceiptApiTest extends ApiRunner { @Test - public void correct() { + void correct() { Optional status = getApi().txs() .receiptStatus("0x513c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76"); assertTrue(status.isPresent()); assertTrue(status.get()); } - @Test(expected = InvalidTxHashException.class) - public void invalidParamWithError() { - getApi().txs().receiptStatus("0x13c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76"); + @Test + void invalidParamWithError() { + assertThrows(InvalidTxHashException.class, + () -> getApi().txs().receiptStatus("0x13c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76")); } @Test - public void correctParamWithEmptyExpectedResult() { + void correctParamWithEmptyExpectedResult() { Optional status = getApi().txs() .receiptStatus("0x113c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76"); assertFalse(status.isPresent()); diff --git a/src/test/java/io/api/manager/QueueManagerTest.java b/src/test/java/io/api/manager/QueueManagerTest.java index 74e674c..7bd53a9 100644 --- a/src/test/java/io/api/manager/QueueManagerTest.java +++ b/src/test/java/io/api/manager/QueueManagerTest.java @@ -4,18 +4,17 @@ import io.api.etherscan.manager.IQueueManager; import io.api.etherscan.manager.impl.FakeQueueManager; import io.api.etherscan.manager.impl.QueueManager; -import org.junit.Test; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ -public class QueueManagerTest extends ApiRunner { +class QueueManagerTest extends ApiRunner { @Test - public void fakeManager() { + void fakeManager() { IQueueManager fakeManager = new FakeQueueManager(); fakeManager.takeTurn(); fakeManager.takeTurn(); @@ -26,16 +25,18 @@ public void fakeManager() { assertNotNull(fakeManager); } - @Test(timeout = 3500) - public void queueManager() { + @Test + @Timeout(3500) + void queueManager() { IQueueManager queueManager = new QueueManager(1, 3); queueManager.takeTurn(); queueManager.takeTurn(); assertNotNull(queueManager); } - @Test(timeout = 4500) - public void queueManagerWithDelay() { + @Test + @Timeout(4500) + void queueManagerWithDelay() { IQueueManager queueManager = new QueueManager(1, 2, 2); queueManager.takeTurn(); queueManager.takeTurn(); @@ -43,7 +44,7 @@ public void queueManagerWithDelay() { } @Test - public void queueManagerTimeout() { + void queueManagerTimeout() { IQueueManager queueManager = new QueueManager(1, 3); queueManager.takeTurn(); long start = System.currentTimeMillis(); diff --git a/src/test/java/io/api/support/AddressUtil.java b/src/test/java/io/api/support/AddressUtil.java index 7949b9e..da04c37 100644 --- a/src/test/java/io/api/support/AddressUtil.java +++ b/src/test/java/io/api/support/AddressUtil.java @@ -5,14 +5,12 @@ import java.util.concurrent.ThreadLocalRandom; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 03.11.2018 */ public class AddressUtil { - public static List genFakeAddresses(int size) { + static List genFakeAddresses(int size) { final List addresses = new ArrayList<>(); for (int i = 0; i < size; i++) addresses.add("0x9327cb34984c" + ThreadLocalRandom.current().nextInt(1000, 9999) + "ec1EA0eAE98Ccf80A74f95B9"); diff --git a/src/test/java/io/api/util/BasicUtilsTests.java b/src/test/java/io/api/util/BasicUtilsTests.java index c35bada..36c22cb 100644 --- a/src/test/java/io/api/util/BasicUtilsTests.java +++ b/src/test/java/io/api/util/BasicUtilsTests.java @@ -1,103 +1,88 @@ package io.api.util; +import static io.api.etherscan.util.BasicUtils.*; + import com.google.gson.Gson; import io.api.ApiRunner; import io.api.etherscan.error.EtherScanException; -import io.api.etherscan.error.ParseException; import io.api.etherscan.model.utility.StringResponseTO; -import org.junit.Test; - import java.util.ArrayList; import java.util.List; - -import static io.api.etherscan.util.BasicUtils.*; +import org.junit.jupiter.api.Test; /** - * ! NO DESCRIPTION ! - * * @author GoodforGod * @since 13.11.2018 */ -public class BasicUtilsTests extends ApiRunner { +class BasicUtilsTests extends ApiRunner { - @Test(expected = EtherScanException.class) - public void responseValidateEmpty() { + @Test + void responseValidateEmpty() { String response = "{\"status\":\"0\",\"message\":\"No ether\",\"result\":\"status\"}"; StringResponseTO responseTO = new Gson().fromJson(response, StringResponseTO.class); - validateTxResponse(responseTO); + + assertThrows(EtherScanException.class, () -> validateTxResponse(responseTO)); } @Test - public void partitionEmpty() { + void partitionEmpty() { ArrayList list = new ArrayList<>(); List> lists = partition(list, 12); assertTrue(lists.isEmpty()); } @Test - public void partitionNullParam() { + void partitionNullParam() { List> lists = partition(null, 12); assertTrue(lists.isEmpty()); } @Test - public void isBlankNull() { + void isBlankNull() { boolean result = isBlank(null); assertTrue(result); } @Test - public void isEmptyCollectionNull() { - List list = null; - boolean result = isEmpty(list); - assertTrue(result); - } - - @Test - public void isEmptyCollectionEmpty() { + void isEmptyCollectionEmpty() { ArrayList list = new ArrayList<>(); boolean result = isEmpty(list); assertTrue(result); } @Test - public void isNotAddressNull() { + void isNotAddressNull() { boolean result = isNotAddress(""); assertTrue(result); } @Test - public void isNotHexNull() { + void isNotHexNull() { boolean result = isNotHex(""); assertTrue(result); } @Test - public void isNotAddressInvalid() { + void isNotAddressInvalid() { boolean result = isNotAddress("125125"); assertTrue(result); } @Test - public void isNotHexInvalid() { + void isNotHexInvalid() { boolean result = isNotHex("1215%"); assertTrue(result); } - @Test(expected = EtherScanException.class) - public void isResponseStatusInvalidThrows() { + @Test + void isResponseStatusInvalidThrows() { StringResponseTO responseTO = new StringResponseTO(); - validateTxResponse(responseTO); + assertThrows(EtherScanException.class, () -> validateTxResponse(responseTO)); } - @Test(expected = EtherScanException.class) - public void isResponseNullThrows() { + @Test + void isResponseNullThrows() { StringResponseTO responseTO = null; - validateTxResponse(responseTO); - } - - @Test(expected = ParseException.class) - public void isThrowParseException() { - throw new ParseException("Test", null, null); + assertThrows(EtherScanException.class, () -> validateTxResponse(responseTO)); } } From 1559a3faf723dbc0d9bba75b1e7ab4a764f262b6 Mon Sep 17 00:00:00 2001 From: Abhay Gupta Date: Thu, 21 Jul 2022 13:31:07 +0530 Subject: [PATCH 08/61] added support for txsToken with contract address too --- .../io/api/etherscan/core/IAccountApi.java | 19 ++++++++++++++ .../core/impl/AccountApiProvider.java | 26 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/src/main/java/io/api/etherscan/core/IAccountApi.java b/src/main/java/io/api/etherscan/core/IAccountApi.java index 25254aa..b3e12fb 100644 --- a/src/main/java/io/api/etherscan/core/IAccountApi.java +++ b/src/main/java/io/api/etherscan/core/IAccountApi.java @@ -110,6 +110,25 @@ public interface IAccountApi { @NotNull List txsToken(String address) throws ApiException; + /** + * All ERC-20 token txs for given address and contract address + * + * @param address get txs for + * @param contractAddress contract address to get txs for + * @param startBlock tx from this blockNumber + * @param endBlock tx to this blockNumber + * @return txs for address + * @throws ApiException parent exception class + */ + @NotNull + List txsToken(String address, String contractAddress, long startBlock, long endBlock) throws ApiException; + + @NotNull + List txsToken(String address, String contractAddress, long startBlock) throws ApiException; + + @NotNull + List txsToken(String address, String contractAddress) throws ApiException; + /** * All ERC-721 (NFT) token txs for given address * diff --git a/src/main/java/io/api/etherscan/core/impl/AccountApiProvider.java b/src/main/java/io/api/etherscan/core/impl/AccountApiProvider.java index 77d8b88..dba413a 100644 --- a/src/main/java/io/api/etherscan/core/impl/AccountApiProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/AccountApiProvider.java @@ -230,6 +230,32 @@ public List txsToken(final String address, final long startBlock, final return getRequestUsingOffset(urlParams, TxTokenResponseTO.class); } + @NotNull + @Override + public List txsToken(final String address, final String contractAddress) throws ApiException { + return txsToken(address, contractAddress, MIN_START_BLOCK); + } + + @NotNull + @Override + public List txsToken(final String address, final String contractAddress, final long startBlock) throws ApiException { + return txsToken(address, contractAddress, startBlock, MAX_END_BLOCK); + } + + @NotNull + @Override + public List txsToken(final String address, final String contractAddress, final long startBlock, final long endBlock) throws ApiException { + BasicUtils.validateAddress(address); + final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); + + final String offsetParam = PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX; + final String blockParam = START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end(); + final String urlParams = ACT_TX_TOKEN_ACTION + offsetParam + ADDRESS_PARAM + address + + CONTRACT_PARAM + contractAddress + blockParam + SORT_ASC_PARAM; + + return getRequestUsingOffset(urlParams, TxTokenResponseTO.class); + } + @NotNull @Override public List txsNftToken(String address) throws ApiException { From 9fb7d918e63deaf349bbd09b5635935db995f4cd Mon Sep 17 00:00:00 2001 From: Abhay Gupta Date: Mon, 14 Nov 2022 17:59:49 +0530 Subject: [PATCH 09/61] gas tracker API implementation --- .../io/api/etherscan/core/IGasTrackerApi.java | 23 +++++++ .../api/etherscan/core/impl/EtherScanApi.java | 7 ++ .../core/impl/GasTrackerApiProvider.java | 39 +++++++++++ .../io/api/etherscan/model/GasOracle.java | 68 +++++++++++++++++++ .../model/utility/GasOracleResponseTO.java | 18 +++++ 5 files changed, 155 insertions(+) create mode 100644 src/main/java/io/api/etherscan/core/IGasTrackerApi.java create mode 100644 src/main/java/io/api/etherscan/core/impl/GasTrackerApiProvider.java create mode 100644 src/main/java/io/api/etherscan/model/GasOracle.java create mode 100644 src/main/java/io/api/etherscan/model/utility/GasOracleResponseTO.java diff --git a/src/main/java/io/api/etherscan/core/IGasTrackerApi.java b/src/main/java/io/api/etherscan/core/IGasTrackerApi.java new file mode 100644 index 0000000..894713f --- /dev/null +++ b/src/main/java/io/api/etherscan/core/IGasTrackerApi.java @@ -0,0 +1,23 @@ +package io.api.etherscan.core; + +import io.api.etherscan.error.ApiException; +import io.api.etherscan.model.GasOracle; +import org.jetbrains.annotations.NotNull; + +/** + * EtherScan - API Descriptions https://docs.etherscan.io/api-endpoints/gas-tracker + * + * @author Abhay Gupta + * @since 14.11.2022 + */ +public interface IGasTrackerApi { + + /** + * GasOracle details + * + * @return fast, suggested gas price + * @throws ApiException parent exception class + */ + @NotNull + GasOracle gasoracle() throws ApiException; +} 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 ba5dd83..957315d 100644 --- a/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java +++ b/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java @@ -34,6 +34,7 @@ public class EtherScanApi implements AutoCloseable { private final IProxyApi proxy; private final IStatisticApi stats; private final ITransactionApi txs; + private final IGasTrackerApi gastracker; public EtherScanApi() { this(DEFAULT_KEY, EthNetwork.MAINNET); @@ -96,6 +97,7 @@ public EtherScanApi(final String apiKey, this.proxy = new ProxyApiProvider(queue, baseUrl, executor); this.stats = new StatisticApiProvider(queue, baseUrl, executor); this.txs = new TransactionApiProvider(queue, baseUrl, executor); + this.gastracker = new GasTrackerApiProvider(queue, baseUrl, executor); } @NotNull @@ -133,6 +135,11 @@ public IStatisticApi stats() { return stats; } + @NotNull + public IGasTrackerApi gastracker() { + return gastracker; + } + @Override public void close() throws Exception { queueManager.close(); diff --git a/src/main/java/io/api/etherscan/core/impl/GasTrackerApiProvider.java b/src/main/java/io/api/etherscan/core/impl/GasTrackerApiProvider.java new file mode 100644 index 0000000..16d2e63 --- /dev/null +++ b/src/main/java/io/api/etherscan/core/impl/GasTrackerApiProvider.java @@ -0,0 +1,39 @@ +package io.api.etherscan.core.impl; + +import io.api.etherscan.core.IGasTrackerApi; +import io.api.etherscan.error.ApiException; +import io.api.etherscan.error.EtherScanException; +import io.api.etherscan.executor.IHttpExecutor; +import io.api.etherscan.manager.IQueueManager; +import io.api.etherscan.model.GasOracle; +import io.api.etherscan.model.utility.GasOracleResponseTO; +import org.jetbrains.annotations.NotNull; + +/** + * GasTracker API Implementation + * + * @see IGasTrackerApi + * + * @author Abhay Gupta + * @since 14.11.2022 + */ +public class GasTrackerApiProvider extends BasicProvider implements IGasTrackerApi { + + private static final String ACT_GAS_ORACLE_PARAM = ACT_PREFIX + "gasoracle"; + + GasTrackerApiProvider(final IQueueManager queue, + final String baseUrl, + final IHttpExecutor executor) { + super(queue, "gastracker", baseUrl, executor); + } + + @NotNull + @Override + public GasOracle gasoracle() throws ApiException { + final GasOracleResponseTO response = getRequest(ACT_GAS_ORACLE_PARAM, GasOracleResponseTO.class); + if (response.getStatus() != 1) + throw new EtherScanException(response); + + return response.getResult(); + } +} diff --git a/src/main/java/io/api/etherscan/model/GasOracle.java b/src/main/java/io/api/etherscan/model/GasOracle.java new file mode 100644 index 0000000..f3f66c2 --- /dev/null +++ b/src/main/java/io/api/etherscan/model/GasOracle.java @@ -0,0 +1,68 @@ +package io.api.etherscan.model; + +import java.math.BigInteger; +import java.util.Objects; + +/** + * ! NO DESCRIPTION ! + * + * @author Abhay Gupta + * @since 14.11.2022 + */ +public class GasOracle { + private Long LastBlock; + private Integer SafeGasPrice; + private Integer ProposeGasPrice; + private Integer FastGasPrice; + private Double suggestBaseFee; + private String gasUsedRatio; + + public Long getLastBlock() { + return LastBlock; + } + + public BigInteger getSafeGasPriceInWei() { + return BigInteger.valueOf(SafeGasPrice).multiply(BigInteger.TEN.pow(9)); + } + + public BigInteger getProposeGasPriceInWei() { + return BigInteger.valueOf(ProposeGasPrice).multiply(BigInteger.TEN.pow(9)); + } + + public BigInteger getFastGasPriceInWei() { + return BigInteger.valueOf(FastGasPrice).multiply(BigInteger.TEN.pow(9)); + } + + public Double getSuggestBaseFee() { + return suggestBaseFee; + } + + public String getGasUsedRatio() { + return gasUsedRatio; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + GasOracle gasOracle = (GasOracle) o; + return LastBlock.equals(gasOracle.LastBlock) && SafeGasPrice.equals(gasOracle.SafeGasPrice) && ProposeGasPrice.equals(gasOracle.ProposeGasPrice) && FastGasPrice.equals(gasOracle.FastGasPrice) && suggestBaseFee.equals(gasOracle.suggestBaseFee) && gasUsedRatio.equals(gasOracle.gasUsedRatio); + } + + @Override + public int hashCode() { + return Objects.hash(LastBlock, SafeGasPrice, ProposeGasPrice, FastGasPrice, suggestBaseFee, gasUsedRatio); + } + + @Override + public String toString() { + return "GasOracle{" + + "LastBlock=" + LastBlock + + ", SafeGasPrice=" + SafeGasPrice + + ", ProposeGasPrice=" + ProposeGasPrice + + ", FastGasPrice=" + FastGasPrice + + ", suggestBaseFee=" + suggestBaseFee + + ", gasUsedRatio='" + gasUsedRatio + '\'' + + '}'; + } +} diff --git a/src/main/java/io/api/etherscan/model/utility/GasOracleResponseTO.java b/src/main/java/io/api/etherscan/model/utility/GasOracleResponseTO.java new file mode 100644 index 0000000..f0c1fd5 --- /dev/null +++ b/src/main/java/io/api/etherscan/model/utility/GasOracleResponseTO.java @@ -0,0 +1,18 @@ +package io.api.etherscan.model.utility; + +import io.api.etherscan.model.GasOracle; + +/** + * ! NO DESCRIPTION ! + * + * @author Abhay Gupta + * @since 14.11.2022 + */ +public class GasOracleResponseTO extends BaseResponseTO { + + private GasOracle result; + + public GasOracle getResult() { + return result; + } +} From 2174387cf8c57e86e4303b5468a258e017c257e6 Mon Sep 17 00:00:00 2001 From: Abhay Gupta Date: Mon, 14 Nov 2022 18:11:34 +0530 Subject: [PATCH 10/61] debug url --- src/main/java/io/api/etherscan/core/impl/BasicProvider.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/io/api/etherscan/core/impl/BasicProvider.java b/src/main/java/io/api/etherscan/core/impl/BasicProvider.java index b36f406..b0fbe68 100644 --- a/src/main/java/io/api/etherscan/core/impl/BasicProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/BasicProvider.java @@ -77,6 +77,7 @@ T convert(final String json, final Class tClass) { String getRequest(final String urlParameters) { queue.takeTurn(); final String url = baseUrl + module + urlParameters; + System.out.println("Url generated: " + url); final String result = executor.get(url); if (BasicUtils.isEmpty(result)) throw new EtherScanException("Server returned null value for GET request at URL - " + url); From bf59b713daddb826c01d292b900f4ce6a462b184 Mon Sep 17 00:00:00 2001 From: Abhay Gupta Date: Mon, 14 Nov 2022 19:01:50 +0530 Subject: [PATCH 11/61] fixed gas oracle base url --- src/main/java/io/api/etherscan/core/impl/EtherScanApi.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 957315d..028c52b 100644 --- a/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java +++ b/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java @@ -88,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; + final String mainnetBaseUrl = "https://" + EthNetwork.MAINNET.getDomain() + ".etherscan." + ending + "/api" + "?apikey=" + apiKey; this.queueManager = queue; this.account = new AccountApiProvider(queue, baseUrl, executor); @@ -97,7 +98,7 @@ public EtherScanApi(final String apiKey, this.proxy = new ProxyApiProvider(queue, baseUrl, executor); this.stats = new StatisticApiProvider(queue, baseUrl, executor); this.txs = new TransactionApiProvider(queue, baseUrl, executor); - this.gastracker = new GasTrackerApiProvider(queue, baseUrl, executor); + this.gastracker = new GasTrackerApiProvider(queue, mainnetBaseUrl, executor); } @NotNull From c462175c09a35d15e0c7641e77e2cbeb5e0e892b Mon Sep 17 00:00:00 2001 From: Abhay Gupta Date: Mon, 14 Nov 2022 19:04:10 +0530 Subject: [PATCH 12/61] removed sout --- src/main/java/io/api/etherscan/core/impl/BasicProvider.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/io/api/etherscan/core/impl/BasicProvider.java b/src/main/java/io/api/etherscan/core/impl/BasicProvider.java index b0fbe68..b36f406 100644 --- a/src/main/java/io/api/etherscan/core/impl/BasicProvider.java +++ b/src/main/java/io/api/etherscan/core/impl/BasicProvider.java @@ -77,7 +77,6 @@ T convert(final String json, final Class tClass) { String getRequest(final String urlParameters) { queue.takeTurn(); final String url = baseUrl + module + urlParameters; - System.out.println("Url generated: " + url); final String result = executor.get(url); if (BasicUtils.isEmpty(result)) throw new EtherScanException("Server returned null value for GET request at URL - " + url); From f3d6858f1633e7523bf759f7bd18039872f48a0b Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Thu, 11 May 2023 23:53:28 +0300 Subject: [PATCH 13/61] [2.0.0-SNAPSHOT] Package refactoring API refactoring Contracts renamed LogQuery refactored EtherScanAPI refactored & builder added --- .github/workflows/gradle.yml | 2 +- README.md | 5 +- build.gradle | 13 +- gradle/wrapper/gradle-wrapper.properties | 2 +- .../java/io/api/etherscan/core/IBlockApi.java | 25 ---- .../io/api/etherscan/core/IContractApi.java | 24 --- .../java/io/api/etherscan/core/ILogsApi.java | 27 ---- .../io/api/etherscan/core/IStatisticApi.java | 44 ------ .../etherscan/core/impl/BasicProvider.java | 99 ------------ .../core/impl/ContractApiProvider.java | 46 ------ .../api/etherscan/core/impl/EtherScanApi.java | 141 ------------------ .../etherscan/core/impl/LogsApiProvider.java | 43 ------ .../io/api/etherscan/error/ApiException.java | 16 -- .../api/etherscan/error/ApiKeyException.java | 12 -- .../etherscan/error/ApiTimeoutException.java | 12 -- .../etherscan/error/ConnectionException.java | 16 -- .../etherscan/error/EtherScanException.java | 23 --- .../etherscan/error/EventModelException.java | 12 -- .../error/InvalidAddressException.java | 12 -- .../error/InvalidDataHexException.java | 12 -- .../error/InvalidTxHashException.java | 12 -- .../etherscan/error/LogQueryException.java | 12 -- .../etherscan/error/RateLimitException.java | 12 -- .../api/etherscan/manager/IQueueManager.java | 16 -- .../etherscan/manager/impl/QueueManager.java | 62 -------- .../io/api/etherscan/model/EthNetwork.java | 25 ---- .../java/io/api/etherscan/model/Uncle.java | 69 --------- .../io/api/etherscan/model/UncleBlock.java | 65 -------- .../etherscan/model/query/IQueryBuilder.java | 15 -- .../etherscan/model/query/impl/LogQuery.java | 28 ---- .../model/query/impl/LogQueryBuilder.java | 83 ----------- .../model/utility/TxTokenResponseTO.java | 11 -- .../model/utility/UncleBlockResponseTO.java | 16 -- .../api/etherscan/AccountAPI.java} | 62 ++++---- .../api/etherscan/AccountAPIProvider.java} | 122 +++++++-------- .../api/etherscan/BasicProvider.java | 93 ++++++++++++ .../io/goodforgod/api/etherscan/BlockAPI.java | 25 ++++ .../api/etherscan/BlockAPIProvider.java} | 29 ++-- .../goodforgod/api/etherscan/ContractAPI.java | 24 +++ .../api/etherscan/ContractAPIProvider.java | 45 ++++++ .../goodforgod/api/etherscan/EthNetwork.java | 14 ++ .../goodforgod/api/etherscan/EthNetworks.java | 29 ++++ .../api/etherscan/EthScanAPIBuilder.java | 71 +++++++++ .../api/etherscan/EtherScanAPI.java | 61 ++++++++ .../api/etherscan/EtherScanAPIProvider.java | 89 +++++++++++ .../io/goodforgod/api/etherscan/LogsAPI.java | 27 ++++ .../api/etherscan/LogsAPIProvider.java | 42 ++++++ .../api/etherscan/ProxyAPI.java} | 70 ++++----- .../api/etherscan/ProxyAPIProvider.java} | 77 +++++----- .../api/etherscan/StatisticAPI.java | 44 ++++++ .../api/etherscan/StatisticAPIProvider.java} | 41 +++-- .../api/etherscan/TransactionAPI.java} | 18 +-- .../etherscan/TransactionAPIProvider.java} | 33 ++-- .../error/ErtherScanLogQueryException.java | 12 ++ .../error/EtherScanConnectionException.java | 16 ++ .../etherscan/error/EtherScanException.java | 16 ++ .../EtherScanInvalidAddressException.java | 12 ++ .../EtherScanInvalidDataHexException.java | 12 ++ .../EtherScanInvalidTxHashException.java | 12 ++ .../error/EtherScanKeyException.java | 12 ++ .../error/EtherScanParseException.java} | 6 +- .../error/EtherScanRateLimitException.java | 12 ++ .../error/EtherScanResponseException.java | 23 +++ .../error/EtherScanTimeoutException.java | 12 ++ .../etherscan/executor/EthHttpClient.java} | 4 +- .../executor/impl/UrlEthHttpClient.java} | 64 ++++---- .../manager/RequestQueueManager.java | 24 +++ .../impl/FakeRequestQueueManager.java} | 6 +- .../impl/SemaphoreRequestQueueManager.java | 53 +++++++ .../api/etherscan/model/Abi.java | 4 +- .../api/etherscan/model/Balance.java | 4 +- .../api/etherscan/model/BaseTx.java | 4 +- .../api/etherscan/model/Block.java | 4 +- .../api/etherscan/model/BlockUncle.java | 128 ++++++++++++++++ .../api/etherscan/model/Log.java | 4 +- .../api/etherscan/model/Price.java | 2 +- .../api/etherscan/model/Status.java | 2 +- .../api/etherscan/model/Supply.java | 2 +- .../api/etherscan/model/TokenBalance.java | 2 +- .../api/etherscan/model/Tx.java | 4 +- .../api/etherscan/model/TxERC20.java} | 6 +- .../api/etherscan/model/TxERC721.java | 71 +++++++++ .../api/etherscan/model/TxInternal.java | 2 +- .../api/etherscan/model/Wei.java | 2 +- .../api/etherscan/model/proxy/BlockProxy.java | 4 +- .../etherscan/model/proxy/ReceiptProxy.java | 6 +- .../api/etherscan/model/proxy/TxProxy.java | 4 +- .../model/proxy/utility/BaseProxyTO.java | 2 +- .../model/proxy/utility/BlockProxyTO.java | 4 +- .../model/proxy/utility/ErrorProxyTO.java | 2 +- .../model/proxy/utility/StringProxyTO.java | 2 +- .../model/proxy/utility/TxInfoProxyTO.java | 4 +- .../model/proxy/utility/TxProxyTO.java | 4 +- .../api/etherscan/model/query/LogOp.java | 2 +- .../api/etherscan/model/query/LogQuery.java | 51 +++++++ .../model/query/LogQueryBuilderImpl.java | 84 +++++++++++ .../etherscan/model/query/LogQueryImpl.java | 30 ++++ .../model/query/LogQueryParams.java} | 12 +- .../model/query/LogTopicBuilder.java | 17 +++ .../model/query}/LogTopicQuadro.java | 33 ++-- .../model/query}/LogTopicSingle.java | 20 +-- .../model/query}/LogTopicTriple.java | 27 ++-- .../etherscan/model/query}/LogTopicTuple.java | 23 +-- .../model/response}/BalanceResponseTO.java | 2 +- .../etherscan/model/response}/BalanceTO.java | 2 +- .../model/response}/BaseListResponseTO.java | 2 +- .../model/response}/BaseResponseTO.java | 4 +- .../etherscan/model/response}/BlockParam.java | 2 +- .../model/response}/BlockResponseTO.java | 4 +- .../model/response}/LogResponseTO.java | 4 +- .../model/response}/PriceResponseTO.java | 4 +- .../response}/ReceiptStatusResponseTO.java | 2 +- .../model/response}/ReceiptStatusTO.java | 2 +- .../model/response}/StatusResponseTO.java | 4 +- .../model/response}/StringResponseTO.java | 2 +- .../model/response/TxERC20ResponseTO.java | 11 ++ .../model/response/TxERC721ResponseTO.java | 11 ++ .../model/response}/TxInternalResponseTO.java | 4 +- .../model/response}/TxResponseTO.java | 4 +- .../model/response/UncleBlockResponseTO.java | 16 ++ .../api/etherscan/util/BasicUtils.java | 32 ++-- src/test/java/io/api/ApiRunner.java | 60 -------- .../io/api/etherscan/EtherScanApiTest.java | 81 ---------- .../java/io/api/manager/QueueManagerTest.java | 55 ------- .../goodforgod/api/etherscan/ApiRunner.java | 66 ++++++++ .../api/etherscan/EtherScanAPITests.java | 76 ++++++++++ .../account/AccountBalanceListTest.java | 12 +- .../etherscan/account/AccountBalanceTest.java | 15 +- .../account/AccountMinedBlocksTest.java | 20 +-- .../account/AccountTokenBalanceTest.java | 22 +-- .../account/AccountTxERC20Test.java} | 26 ++-- .../account/AccountTxInternalByHashTest.java | 16 +- .../account/AccountTxInternalTest.java | 10 +- .../account/AccountTxRc721TokenTest.java | 26 ++-- .../api/etherscan/account/AccountTxsTest.java | 11 +- .../api/etherscan/block/BlockApiTest.java | 14 +- .../etherscan/contract/ContractApiTest.java | 10 +- .../etherscan/logs/LogQueryBuilderTest.java | 141 +++++++++--------- .../api/etherscan/logs/LogsApiTest.java | 27 ++-- .../SemaphoreRequestQueueManagerTest.java | 56 +++++++ .../etherscan/proxy/ProxyBlockApiTest.java | 19 +-- .../proxy/ProxyBlockLastNoApiTest.java | 4 +- .../proxy/ProxyBlockUncleApiTest.java | 6 +- .../api/etherscan/proxy/ProxyCallApiTest.java | 20 +-- .../api/etherscan/proxy/ProxyCodeApiTest.java | 11 +- .../api/etherscan/proxy/ProxyGasApiTest.java | 8 +- .../etherscan/proxy/ProxyStorageApiTest.java | 8 +- .../api/etherscan/proxy/ProxyTxApiTest.java | 10 +- .../etherscan/proxy/ProxyTxCountApiTest.java | 8 +- .../proxy/ProxyTxReceiptApiTest.java | 10 +- .../proxy/ProxyTxSendRawApiTest.java | 12 +- .../statistic/StatisticPriceApiTest.java | 6 +- .../statistic/StatisticSupplyApiTest.java | 6 +- .../StatisticTokenSupplyApiTest.java | 9 +- .../api/etherscan}/support/AddressUtil.java | 2 +- .../transaction/TransactionExecApiTest.java | 16 +- .../TransactionReceiptApiTest.java | 14 +- .../api/etherscan}/util/BasicUtilsTests.java | 16 +- 158 files changed, 2083 insertions(+), 1854 deletions(-) delete mode 100644 src/main/java/io/api/etherscan/core/IBlockApi.java delete mode 100644 src/main/java/io/api/etherscan/core/IContractApi.java delete mode 100644 src/main/java/io/api/etherscan/core/ILogsApi.java delete mode 100644 src/main/java/io/api/etherscan/core/IStatisticApi.java delete mode 100644 src/main/java/io/api/etherscan/core/impl/BasicProvider.java delete mode 100644 src/main/java/io/api/etherscan/core/impl/ContractApiProvider.java delete mode 100644 src/main/java/io/api/etherscan/core/impl/EtherScanApi.java delete mode 100644 src/main/java/io/api/etherscan/core/impl/LogsApiProvider.java delete mode 100644 src/main/java/io/api/etherscan/error/ApiException.java delete mode 100644 src/main/java/io/api/etherscan/error/ApiKeyException.java delete mode 100644 src/main/java/io/api/etherscan/error/ApiTimeoutException.java delete mode 100644 src/main/java/io/api/etherscan/error/ConnectionException.java delete mode 100644 src/main/java/io/api/etherscan/error/EtherScanException.java delete mode 100644 src/main/java/io/api/etherscan/error/EventModelException.java delete mode 100644 src/main/java/io/api/etherscan/error/InvalidAddressException.java delete mode 100644 src/main/java/io/api/etherscan/error/InvalidDataHexException.java delete mode 100644 src/main/java/io/api/etherscan/error/InvalidTxHashException.java delete mode 100644 src/main/java/io/api/etherscan/error/LogQueryException.java delete mode 100644 src/main/java/io/api/etherscan/error/RateLimitException.java delete mode 100644 src/main/java/io/api/etherscan/manager/IQueueManager.java delete mode 100644 src/main/java/io/api/etherscan/manager/impl/QueueManager.java delete mode 100644 src/main/java/io/api/etherscan/model/EthNetwork.java delete mode 100644 src/main/java/io/api/etherscan/model/Uncle.java delete mode 100644 src/main/java/io/api/etherscan/model/UncleBlock.java delete mode 100644 src/main/java/io/api/etherscan/model/query/IQueryBuilder.java delete mode 100644 src/main/java/io/api/etherscan/model/query/impl/LogQuery.java delete mode 100644 src/main/java/io/api/etherscan/model/query/impl/LogQueryBuilder.java delete mode 100644 src/main/java/io/api/etherscan/model/utility/TxTokenResponseTO.java delete mode 100644 src/main/java/io/api/etherscan/model/utility/UncleBlockResponseTO.java rename src/main/java/io/{api/etherscan/core/IAccountApi.java => goodforgod/api/etherscan/AccountAPI.java} (55%) rename src/main/java/io/{api/etherscan/core/impl/AccountApiProvider.java => goodforgod/api/etherscan/AccountAPIProvider.java} (59%) create mode 100644 src/main/java/io/goodforgod/api/etherscan/BasicProvider.java create mode 100644 src/main/java/io/goodforgod/api/etherscan/BlockAPI.java rename src/main/java/io/{api/etherscan/core/impl/BlockApiProvider.java => goodforgod/api/etherscan/BlockAPIProvider.java} (53%) create mode 100644 src/main/java/io/goodforgod/api/etherscan/ContractAPI.java create mode 100644 src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java create mode 100644 src/main/java/io/goodforgod/api/etherscan/EthNetwork.java create mode 100644 src/main/java/io/goodforgod/api/etherscan/EthNetworks.java create mode 100644 src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java create mode 100644 src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java create mode 100644 src/main/java/io/goodforgod/api/etherscan/EtherScanAPIProvider.java create mode 100644 src/main/java/io/goodforgod/api/etherscan/LogsAPI.java create mode 100644 src/main/java/io/goodforgod/api/etherscan/LogsAPIProvider.java rename src/main/java/io/{api/etherscan/core/IProxyApi.java => goodforgod/api/etherscan/ProxyAPI.java} (63%) rename src/main/java/io/{api/etherscan/core/impl/ProxyApiProvider.java => goodforgod/api/etherscan/ProxyAPIProvider.java} (75%) create mode 100644 src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java rename src/main/java/io/{api/etherscan/core/impl/StatisticApiProvider.java => goodforgod/api/etherscan/StatisticAPIProvider.java} (54%) rename src/main/java/io/{api/etherscan/core/ITransactionApi.java => goodforgod/api/etherscan/TransactionAPI.java} (51%) rename src/main/java/io/{api/etherscan/core/impl/TransactionApiProvider.java => goodforgod/api/etherscan/TransactionAPIProvider.java} (60%) create mode 100644 src/main/java/io/goodforgod/api/etherscan/error/ErtherScanLogQueryException.java create mode 100644 src/main/java/io/goodforgod/api/etherscan/error/EtherScanConnectionException.java create mode 100644 src/main/java/io/goodforgod/api/etherscan/error/EtherScanException.java create mode 100644 src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidAddressException.java create mode 100644 src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidDataHexException.java create mode 100644 src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidTxHashException.java create mode 100644 src/main/java/io/goodforgod/api/etherscan/error/EtherScanKeyException.java rename src/main/java/io/{api/etherscan/error/ParseException.java => goodforgod/api/etherscan/error/EtherScanParseException.java} (52%) create mode 100644 src/main/java/io/goodforgod/api/etherscan/error/EtherScanRateLimitException.java create mode 100644 src/main/java/io/goodforgod/api/etherscan/error/EtherScanResponseException.java create mode 100644 src/main/java/io/goodforgod/api/etherscan/error/EtherScanTimeoutException.java rename src/main/java/io/{api/etherscan/executor/IHttpExecutor.java => goodforgod/api/etherscan/executor/EthHttpClient.java} (84%) rename src/main/java/io/{api/etherscan/executor/impl/HttpExecutor.java => goodforgod/api/etherscan/executor/impl/UrlEthHttpClient.java} (66%) create mode 100644 src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java rename src/main/java/io/{api/etherscan/manager/impl/FakeQueueManager.java => goodforgod/api/etherscan/manager/impl/FakeRequestQueueManager.java} (65%) create mode 100644 src/main/java/io/goodforgod/api/etherscan/manager/impl/SemaphoreRequestQueueManager.java rename src/main/java/io/{ => goodforgod}/api/etherscan/model/Abi.java (94%) rename src/main/java/io/{ => goodforgod}/api/etherscan/model/Balance.java (94%) rename src/main/java/io/{ => goodforgod}/api/etherscan/model/BaseTx.java (97%) rename src/main/java/io/{ => goodforgod}/api/etherscan/model/Block.java (94%) create mode 100644 src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java rename src/main/java/io/{ => goodforgod}/api/etherscan/model/Log.java (98%) rename src/main/java/io/{ => goodforgod}/api/etherscan/model/Price.java (98%) rename src/main/java/io/{ => goodforgod}/api/etherscan/model/Status.java (96%) rename src/main/java/io/{ => goodforgod}/api/etherscan/model/Supply.java (81%) rename src/main/java/io/{ => goodforgod}/api/etherscan/model/TokenBalance.java (96%) rename src/main/java/io/{ => goodforgod}/api/etherscan/model/Tx.java (96%) rename src/main/java/io/{api/etherscan/model/TxToken.java => goodforgod/api/etherscan/model/TxERC20.java} (93%) create mode 100644 src/main/java/io/goodforgod/api/etherscan/model/TxERC721.java rename src/main/java/io/{ => goodforgod}/api/etherscan/model/TxInternal.java (97%) rename src/main/java/io/{ => goodforgod}/api/etherscan/model/Wei.java (96%) rename src/main/java/io/{ => goodforgod}/api/etherscan/model/proxy/BlockProxy.java (98%) rename src/main/java/io/{ => goodforgod}/api/etherscan/model/proxy/ReceiptProxy.java (96%) rename src/main/java/io/{ => goodforgod}/api/etherscan/model/proxy/TxProxy.java (97%) rename src/main/java/io/{ => goodforgod}/api/etherscan/model/proxy/utility/BaseProxyTO.java (86%) rename src/main/java/io/{ => goodforgod}/api/etherscan/model/proxy/utility/BlockProxyTO.java (63%) rename src/main/java/io/{ => goodforgod}/api/etherscan/model/proxy/utility/ErrorProxyTO.java (81%) rename src/main/java/io/{ => goodforgod}/api/etherscan/model/proxy/utility/StringProxyTO.java (77%) rename src/main/java/io/{ => goodforgod}/api/etherscan/model/proxy/utility/TxInfoProxyTO.java (63%) rename src/main/java/io/{ => goodforgod}/api/etherscan/model/proxy/utility/TxProxyTO.java (62%) rename src/main/java/io/{ => goodforgod}/api/etherscan/model/query/LogOp.java (86%) create mode 100644 src/main/java/io/goodforgod/api/etherscan/model/query/LogQuery.java create mode 100644 src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryBuilderImpl.java create mode 100644 src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryImpl.java rename src/main/java/io/{api/etherscan/model/query/impl/BaseLogQuery.java => goodforgod/api/etherscan/model/query/LogQueryParams.java} (79%) create mode 100644 src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicBuilder.java rename src/main/java/io/{api/etherscan/model/query/impl => goodforgod/api/etherscan/model/query}/LogTopicQuadro.java (71%) rename src/main/java/io/{api/etherscan/model/query/impl => goodforgod/api/etherscan/model/query}/LogTopicSingle.java (53%) rename src/main/java/io/{api/etherscan/model/query/impl => goodforgod/api/etherscan/model/query}/LogTopicTriple.java (68%) rename src/main/java/io/{api/etherscan/model/query/impl => goodforgod/api/etherscan/model/query}/LogTopicTuple.java (63%) rename src/main/java/io/{api/etherscan/model/utility => goodforgod/api/etherscan/model/response}/BalanceResponseTO.java (70%) rename src/main/java/io/{api/etherscan/model/utility => goodforgod/api/etherscan/model/response}/BalanceTO.java (83%) rename src/main/java/io/{api/etherscan/model/utility => goodforgod/api/etherscan/model/response}/BaseListResponseTO.java (82%) rename src/main/java/io/{api/etherscan/model/utility => goodforgod/api/etherscan/model/response}/BaseResponseTO.java (77%) rename src/main/java/io/{api/etherscan/model/utility => goodforgod/api/etherscan/model/response}/BlockParam.java (88%) rename src/main/java/io/{api/etherscan/model/utility => goodforgod/api/etherscan/model/response}/BlockResponseTO.java (54%) rename src/main/java/io/{api/etherscan/model/utility => goodforgod/api/etherscan/model/response}/LogResponseTO.java (54%) rename src/main/java/io/{api/etherscan/model/utility => goodforgod/api/etherscan/model/response}/PriceResponseTO.java (66%) rename src/main/java/io/{api/etherscan/model/utility => goodforgod/api/etherscan/model/response}/ReceiptStatusResponseTO.java (81%) rename src/main/java/io/{api/etherscan/model/utility => goodforgod/api/etherscan/model/response}/ReceiptStatusTO.java (77%) rename src/main/java/io/{api/etherscan/model/utility => goodforgod/api/etherscan/model/response}/StatusResponseTO.java (66%) rename src/main/java/io/{api/etherscan/model/utility => goodforgod/api/etherscan/model/response}/StringResponseTO.java (79%) create mode 100644 src/main/java/io/goodforgod/api/etherscan/model/response/TxERC20ResponseTO.java create mode 100644 src/main/java/io/goodforgod/api/etherscan/model/response/TxERC721ResponseTO.java rename src/main/java/io/{api/etherscan/model/utility => goodforgod/api/etherscan/model/response}/TxInternalResponseTO.java (55%) rename src/main/java/io/{api/etherscan/model/utility => goodforgod/api/etherscan/model/response}/TxResponseTO.java (54%) create mode 100644 src/main/java/io/goodforgod/api/etherscan/model/response/UncleBlockResponseTO.java rename src/main/java/io/{ => goodforgod}/api/etherscan/util/BasicUtils.java (80%) delete mode 100644 src/test/java/io/api/ApiRunner.java delete mode 100644 src/test/java/io/api/etherscan/EtherScanApiTest.java delete mode 100644 src/test/java/io/api/manager/QueueManagerTest.java create mode 100644 src/test/java/io/goodforgod/api/etherscan/ApiRunner.java create mode 100644 src/test/java/io/goodforgod/api/etherscan/EtherScanAPITests.java rename src/test/java/io/{ => goodforgod}/api/etherscan/account/AccountBalanceListTest.java (88%) rename src/test/java/io/{ => goodforgod}/api/etherscan/account/AccountBalanceTest.java (67%) rename src/test/java/io/{ => goodforgod}/api/etherscan/account/AccountMinedBlocksTest.java (65%) rename src/test/java/io/{ => goodforgod}/api/etherscan/account/AccountTokenBalanceTest.java (63%) rename src/test/java/io/{api/etherscan/account/AccountTxTokenTest.java => goodforgod/api/etherscan/account/AccountTxERC20Test.java} (72%) rename src/test/java/io/{ => goodforgod}/api/etherscan/account/AccountTxInternalByHashTest.java (81%) rename src/test/java/io/{ => goodforgod}/api/etherscan/account/AccountTxInternalTest.java (86%) rename src/test/java/io/{ => goodforgod}/api/etherscan/account/AccountTxRc721TokenTest.java (65%) rename src/test/java/io/{ => goodforgod}/api/etherscan/account/AccountTxsTest.java (87%) rename src/test/java/io/{ => goodforgod}/api/etherscan/block/BlockApiTest.java (82%) rename src/test/java/io/{ => goodforgod}/api/etherscan/contract/ContractApiTest.java (78%) rename src/test/java/io/{ => goodforgod}/api/etherscan/logs/LogQueryBuilderTest.java (59%) rename src/test/java/io/{ => goodforgod}/api/etherscan/logs/LogsApiTest.java (67%) create mode 100644 src/test/java/io/goodforgod/api/etherscan/manager/SemaphoreRequestQueueManagerTest.java rename src/test/java/io/{ => goodforgod}/api/etherscan/proxy/ProxyBlockApiTest.java (76%) rename src/test/java/io/{ => goodforgod}/api/etherscan/proxy/ProxyBlockLastNoApiTest.java (75%) rename src/test/java/io/{ => goodforgod}/api/etherscan/proxy/ProxyBlockUncleApiTest.java (83%) rename src/test/java/io/{ => goodforgod}/api/etherscan/proxy/ProxyCallApiTest.java (54%) rename src/test/java/io/{ => goodforgod}/api/etherscan/proxy/ProxyCodeApiTest.java (66%) rename src/test/java/io/{ => goodforgod}/api/etherscan/proxy/ProxyGasApiTest.java (79%) rename src/test/java/io/{ => goodforgod}/api/etherscan/proxy/ProxyStorageApiTest.java (76%) rename src/test/java/io/{ => goodforgod}/api/etherscan/proxy/ProxyTxApiTest.java (88%) rename src/test/java/io/{ => goodforgod}/api/etherscan/proxy/ProxyTxCountApiTest.java (81%) rename src/test/java/io/{ => goodforgod}/api/etherscan/proxy/ProxyTxReceiptApiTest.java (85%) rename src/test/java/io/{ => goodforgod}/api/etherscan/proxy/ProxyTxSendRawApiTest.java (62%) rename src/test/java/io/{ => goodforgod}/api/etherscan/statistic/StatisticPriceApiTest.java (81%) rename src/test/java/io/{ => goodforgod}/api/etherscan/statistic/StatisticSupplyApiTest.java (82%) rename src/test/java/io/{ => goodforgod}/api/etherscan/statistic/StatisticTokenSupplyApiTest.java (67%) rename src/test/java/io/{api => goodforgod/api/etherscan}/support/AddressUtil.java (98%) rename src/test/java/io/{ => goodforgod}/api/etherscan/transaction/TransactionExecApiTest.java (66%) rename src/test/java/io/{ => goodforgod}/api/etherscan/transaction/TransactionReceiptApiTest.java (61%) rename src/test/java/io/{api => goodforgod/api/etherscan}/util/BasicUtilsTests.java (75%) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 613a39e..e4c7620 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - java: [ '11' ] + java: [ '11', '17' ] name: Java ${{ matrix.java }} setup steps: diff --git a/README.md b/README.md index 258b4d8..cd981ca 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # Java EtherScan API +[![Minimum required Java version](https://img.shields.io/badge/Java-1.8%2B-blue?logo=openjdk)](https://openjdk.org/projects/jdk8/) [![GitHub Action](https://github.com/goodforgod/java-etherscan-api/workflows/Java%20CI/badge.svg)](https://github.com/GoodforGod/java-etherscan-api/actions?query=workflow%3A%22Java+CI%22) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=GoodforGod_java-etherscan-api&metric=coverage)](https://sonarcloud.io/dashboard?id=GoodforGod_java-etherscan-api) [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=GoodforGod_java-etherscan-api&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=GoodforGod_java-etherscan-api) @@ -14,7 +15,7 @@ Library supports all available EtherScan *API* calls for all available *Ethereum **Gradle** ```groovy -implementation "com.github.goodforgod:java-etherscan-api:1.3.1" +implementation "com.github.goodforgod:java-etherscan-api:2.0.0-SNAPSHOT" ``` **Maven** @@ -22,7 +23,7 @@ implementation "com.github.goodforgod:java-etherscan-api:1.3.1" com.github.goodforgod java-etherscan-api - 1.3.1 + 2.0.0-SNAPSHOT ``` diff --git a/build.gradle b/build.gradle index 410d374..3d766c2 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { id "maven-publish" id "org.sonarqube" version "3.3" - id "com.diffplug.spotless" version "6.1.0" + id "com.diffplug.spotless" version "6.12.0" } repositories { @@ -19,13 +19,12 @@ sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 dependencies { - implementation "org.jetbrains:annotations:23.0.0" - implementation "com.google.code.gson:gson:2.9.0" - implementation "io.goodforgod:gson-configuration:1.4.1" + compileOnly "org.jetbrains:annotations:23.0.0" + implementation "io.goodforgod:gson-configuration:2.0.0" - testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.8.2" - testImplementation "org.junit.jupiter:junit-jupiter-api:5.8.2" - testImplementation "org.junit.jupiter:junit-jupiter-params:5.8.2" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.9.3" + testImplementation "org.junit.jupiter:junit-jupiter-api:5.9.3" + testImplementation "org.junit.jupiter:junit-jupiter-params:5.9.3" } test { diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 41dfb87..070cb70 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/io/api/etherscan/core/IBlockApi.java b/src/main/java/io/api/etherscan/core/IBlockApi.java deleted file mode 100644 index df4ae96..0000000 --- a/src/main/java/io/api/etherscan/core/IBlockApi.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.api.etherscan.core; - -import io.api.etherscan.error.ApiException; -import io.api.etherscan.model.UncleBlock; -import java.util.Optional; -import org.jetbrains.annotations.NotNull; - -/** - * EtherScan - API Descriptions https://etherscan.io/apis#blocks - * - * @author GoodforGod - * @since 30.10.2018 - */ -public interface IBlockApi { - - /** - * Return uncle blocks - * - * @param blockNumber block number form 0 to last - * @return optional uncle blocks - * @throws ApiException parent exception class - */ - @NotNull - Optional uncles(long blockNumber) throws ApiException; -} diff --git a/src/main/java/io/api/etherscan/core/IContractApi.java b/src/main/java/io/api/etherscan/core/IContractApi.java deleted file mode 100644 index 3e9388d..0000000 --- a/src/main/java/io/api/etherscan/core/IContractApi.java +++ /dev/null @@ -1,24 +0,0 @@ -package io.api.etherscan.core; - -import io.api.etherscan.error.ApiException; -import io.api.etherscan.model.Abi; -import org.jetbrains.annotations.NotNull; - -/** - * EtherScan - API Descriptions https://etherscan.io/apis#contracts - * - * @author GoodforGod - * @since 28.10.2018 - */ -public interface IContractApi { - - /** - * Get Verified Contract Sources - * - * @param address to verify - * @return ABI verified - * @throws ApiException parent exception class - */ - @NotNull - Abi contractAbi(String address) throws ApiException; -} diff --git a/src/main/java/io/api/etherscan/core/ILogsApi.java b/src/main/java/io/api/etherscan/core/ILogsApi.java deleted file mode 100644 index 7ecd986..0000000 --- a/src/main/java/io/api/etherscan/core/ILogsApi.java +++ /dev/null @@ -1,27 +0,0 @@ -package io.api.etherscan.core; - -import io.api.etherscan.error.ApiException; -import io.api.etherscan.model.Log; -import io.api.etherscan.model.query.impl.LogQuery; -import java.util.List; -import org.jetbrains.annotations.NotNull; - -/** - * EtherScan - API Descriptions https://etherscan.io/apis#logs - * - * @author GoodforGod - * @since 30.10.2018 - */ -public interface ILogsApi { - - /** - * alternative to the native eth_getLogs Read at EtherScan API description for full info! - * - * @param query build log query - * @return logs according to query - * @throws ApiException parent exception class - * @see io.api.etherscan.model.query.impl.LogQueryBuilder - */ - @NotNull - List logs(LogQuery query) throws ApiException; -} diff --git a/src/main/java/io/api/etherscan/core/IStatisticApi.java b/src/main/java/io/api/etherscan/core/IStatisticApi.java deleted file mode 100644 index ffd633d..0000000 --- a/src/main/java/io/api/etherscan/core/IStatisticApi.java +++ /dev/null @@ -1,44 +0,0 @@ -package io.api.etherscan.core; - -import io.api.etherscan.error.ApiException; -import io.api.etherscan.model.Price; -import io.api.etherscan.model.Supply; -import java.math.BigInteger; -import org.jetbrains.annotations.NotNull; - -/** - * EtherScan - API Descriptions https://etherscan.io/apis#stats - * - * @author GoodforGod - * @since 30.10.2018 - */ -public interface IStatisticApi { - - /** - * ERC20 token total Supply - * - * @param contract contract address - * @return token supply for specified contract - * @throws ApiException parent exception class - */ - @NotNull - BigInteger supply(String contract) throws ApiException; - - /** - * Eth total supply - * - * @return total ETH supply for moment - * @throws ApiException parent exception class - */ - @NotNull - Supply supply() throws ApiException; - - /** - * Eth last USD and BTC price - * - * @return last usd/btc price for ETH - * @throws ApiException parent exception class - */ - @NotNull - Price lastPrice() throws ApiException; -} diff --git a/src/main/java/io/api/etherscan/core/impl/BasicProvider.java b/src/main/java/io/api/etherscan/core/impl/BasicProvider.java deleted file mode 100644 index ada41bb..0000000 --- a/src/main/java/io/api/etherscan/core/impl/BasicProvider.java +++ /dev/null @@ -1,99 +0,0 @@ -package io.api.etherscan.core.impl; - -import com.google.gson.*; -import io.api.etherscan.error.ApiException; -import io.api.etherscan.error.EtherScanException; -import io.api.etherscan.error.ParseException; -import io.api.etherscan.error.RateLimitException; -import io.api.etherscan.executor.IHttpExecutor; -import io.api.etherscan.manager.IQueueManager; -import io.api.etherscan.model.utility.StringResponseTO; -import io.api.etherscan.util.BasicUtils; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.util.Map; - -/** - * Base provider for API Implementations - * - * @author GoodforGod - * @see EtherScanApi - * @since 28.10.2018 - */ -abstract class BasicProvider { - - static final int MAX_END_BLOCK = Integer.MAX_VALUE; - static final int MIN_START_BLOCK = 0; - - static final String ACT_PREFIX = "&action="; - - private final String module; - private final String baseUrl; - private final IHttpExecutor executor; - private final IQueueManager queue; - private final Gson gson; - - BasicProvider(final IQueueManager queue, - final String module, - final String baseUrl, - final IHttpExecutor executor) { - this.queue = queue; - this.module = "&module=" + module; - this.baseUrl = baseUrl; - this.executor = executor; - this.gson = new GsonBuilder() - .registerTypeAdapter(LocalDateTime.class, (JsonSerializer) (src, t, c) -> new JsonPrimitive("")) - .registerTypeAdapter(LocalDate.class, (JsonSerializer) (src, t, context) -> new JsonPrimitive("")) - .registerTypeAdapter(LocalDateTime.class, (JsonDeserializer) (json, t, c) -> null) - .registerTypeAdapter(LocalDate.class, (JsonDeserializer) (json, t, c) -> null) - .create(); - } - - T convert(final String json, final Class tClass) { - try { - final T t = gson.fromJson(json, tClass); - if (t instanceof StringResponseTO && ((StringResponseTO) t).getResult().startsWith("Max rate limit reached")) { - throw new RateLimitException(((StringResponseTO) t).getResult()); - } - - return t; - } catch (Exception e) { - try { - final Map map = gson.fromJson(json, Map.class); - final Object result = map.get("result"); - if (result instanceof String && ((String) result).startsWith("Max rate limit reached")) - throw new RateLimitException(((String) result)); - - throw new ParseException(e.getMessage() + ", for response: " + json, e.getCause(), json); - } catch (ApiException ex) { - throw ex; - } catch (Exception ex) { - throw new ParseException(e.getMessage() + ", for response: " + json, e.getCause(), json); - } - } - } - - String getRequest(final String urlParameters) { - queue.takeTurn(); - final String url = baseUrl + module + urlParameters; - final String result = executor.get(url); - if (BasicUtils.isEmpty(result)) - throw new EtherScanException("Server returned null value for GET request at URL - " + url); - - return result; - } - - String postRequest(final String urlParameters, final String dataToPost) { - queue.takeTurn(); - final String url = baseUrl + module + urlParameters; - return executor.post(url, dataToPost); - } - - T getRequest(final String urlParameters, final Class tClass) { - return convert(getRequest(urlParameters), tClass); - } - - T postRequest(final String urlParameters, final String dataToPost, final Class tClass) { - return convert(postRequest(urlParameters, dataToPost), tClass); - } -} diff --git a/src/main/java/io/api/etherscan/core/impl/ContractApiProvider.java b/src/main/java/io/api/etherscan/core/impl/ContractApiProvider.java deleted file mode 100644 index 2e7cbea..0000000 --- a/src/main/java/io/api/etherscan/core/impl/ContractApiProvider.java +++ /dev/null @@ -1,46 +0,0 @@ -package io.api.etherscan.core.impl; - -import io.api.etherscan.core.IContractApi; -import io.api.etherscan.error.ApiException; -import io.api.etherscan.error.EtherScanException; -import io.api.etherscan.executor.IHttpExecutor; -import io.api.etherscan.manager.IQueueManager; -import io.api.etherscan.model.Abi; -import io.api.etherscan.model.utility.StringResponseTO; -import io.api.etherscan.util.BasicUtils; -import org.jetbrains.annotations.NotNull; - -/** - * Contract API Implementation - * - * @see IContractApi - * @author GoodforGod - * @since 28.10.2018 - */ -public class ContractApiProvider extends BasicProvider implements IContractApi { - - private static final String ACT_ABI_PARAM = ACT_PREFIX + "getabi"; - - private static final String ADDRESS_PARAM = "&address="; - - ContractApiProvider(final IQueueManager queueManager, - final String baseUrl, - final IHttpExecutor executor) { - super(queueManager, "contract", baseUrl, executor); - } - - @NotNull - @Override - public Abi contractAbi(final String address) throws ApiException { - BasicUtils.validateAddress(address); - - final String urlParam = ACT_ABI_PARAM + ADDRESS_PARAM + address; - final StringResponseTO response = getRequest(urlParam, StringResponseTO.class); - if (response.getStatus() != 1 && "NOTOK".equals(response.getMessage())) - throw new EtherScanException(response); - - return (response.getResult().startsWith("Contract sou")) - ? Abi.nonVerified() - : Abi.verified(response.getResult()); - } -} diff --git a/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java b/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java deleted file mode 100644 index aac428b..0000000 --- a/src/main/java/io/api/etherscan/core/impl/EtherScanApi.java +++ /dev/null @@ -1,141 +0,0 @@ -package io.api.etherscan.core.impl; - -import io.api.etherscan.core.*; -import io.api.etherscan.error.ApiException; -import io.api.etherscan.error.ApiKeyException; -import io.api.etherscan.executor.IHttpExecutor; -import io.api.etherscan.executor.impl.HttpExecutor; -import io.api.etherscan.manager.IQueueManager; -import io.api.etherscan.manager.impl.FakeQueueManager; -import io.api.etherscan.manager.impl.QueueManager; -import io.api.etherscan.model.EthNetwork; -import io.api.etherscan.util.BasicUtils; -import java.util.function.Supplier; -import org.jetbrains.annotations.NotNull; - -/** - * EtherScan full API Description https://etherscan.io/apis - * - * @author GoodforGod - * @since 28.10.2018 - */ -public class EtherScanApi implements AutoCloseable { - - private static final Supplier DEFAULT_SUPPLIER = HttpExecutor::new; - - public static final String DEFAULT_KEY = "YourApiKeyToken"; - - private final IQueueManager queueManager; - private final IAccountApi account; - private final IBlockApi block; - private final IContractApi contract; - private final ILogsApi logs; - private final IProxyApi proxy; - private final IStatisticApi stats; - private final ITransactionApi txs; - - public EtherScanApi() { - this(DEFAULT_KEY, EthNetwork.MAINNET); - } - - public EtherScanApi(final EthNetwork network) { - this(DEFAULT_KEY, network); - } - - public EtherScanApi(final String apiKey) { - this(apiKey, EthNetwork.MAINNET); - } - - public EtherScanApi(final EthNetwork network, - final Supplier executorSupplier) { - this(DEFAULT_KEY, network, executorSupplier); - } - - public EtherScanApi(final String apiKey, - final EthNetwork network, - final IQueueManager queue) { - this(apiKey, network, DEFAULT_SUPPLIER, queue); - } - - public EtherScanApi(final String apiKey, - final EthNetwork network) { - this(apiKey, network, DEFAULT_SUPPLIER); - } - - public EtherScanApi(final String apiKey, - final EthNetwork network, - final Supplier executorSupplier) { - this(apiKey, network, executorSupplier, - DEFAULT_KEY.equals(apiKey) - ? QueueManager.DEFAULT_KEY_QUEUE - : new FakeQueueManager()); - } - - public EtherScanApi(final String apiKey, - final EthNetwork network, - final Supplier executorSupplier, - final IQueueManager queue) { - if (BasicUtils.isBlank(apiKey)) - throw new ApiKeyException("API key can not be null or empty"); - - if (network == null) - throw new ApiException("Ethereum Network is set to NULL value"); - - // EtherScan 1request\5sec limit support by queue manager - final IHttpExecutor executor = executorSupplier.get(); - - final String ending = EthNetwork.TOBALABA.equals(network) - ? "com" - : "io"; - final String baseUrl = "https://" + network.getDomain() + ".etherscan." + ending + "/api" + "?apikey=" + apiKey; - - this.queueManager = queue; - this.account = new AccountApiProvider(queue, baseUrl, executor); - this.block = new BlockApiProvider(queue, baseUrl, executor); - this.contract = new ContractApiProvider(queue, baseUrl, executor); - this.logs = new LogsApiProvider(queue, baseUrl, executor); - this.proxy = new ProxyApiProvider(queue, baseUrl, executor); - this.stats = new StatisticApiProvider(queue, baseUrl, executor); - this.txs = new TransactionApiProvider(queue, baseUrl, executor); - } - - @NotNull - public IAccountApi account() { - return account; - } - - @NotNull - public IContractApi contract() { - return contract; - } - - @NotNull - public ITransactionApi txs() { - return txs; - } - - @NotNull - public IBlockApi block() { - return block; - } - - @NotNull - public ILogsApi logs() { - return logs; - } - - @NotNull - public IProxyApi proxy() { - return proxy; - } - - @NotNull - public IStatisticApi stats() { - return stats; - } - - @Override - public void close() throws Exception { - queueManager.close(); - } -} diff --git a/src/main/java/io/api/etherscan/core/impl/LogsApiProvider.java b/src/main/java/io/api/etherscan/core/impl/LogsApiProvider.java deleted file mode 100644 index 04f9bb7..0000000 --- a/src/main/java/io/api/etherscan/core/impl/LogsApiProvider.java +++ /dev/null @@ -1,43 +0,0 @@ -package io.api.etherscan.core.impl; - -import io.api.etherscan.core.ILogsApi; -import io.api.etherscan.error.ApiException; -import io.api.etherscan.executor.IHttpExecutor; -import io.api.etherscan.manager.IQueueManager; -import io.api.etherscan.model.Log; -import io.api.etherscan.model.query.impl.LogQuery; -import io.api.etherscan.model.utility.LogResponseTO; -import io.api.etherscan.util.BasicUtils; -import java.util.Collections; -import java.util.List; -import org.jetbrains.annotations.NotNull; - -/** - * Logs API Implementation - * - * @see ILogsApi - * @author GoodforGod - * @since 28.10.2018 - */ -public class LogsApiProvider extends BasicProvider implements ILogsApi { - - private static final String ACT_LOGS_PARAM = ACT_PREFIX + "getLogs"; - - LogsApiProvider(final IQueueManager queue, - final String baseUrl, - final IHttpExecutor executor) { - super(queue, "logs", baseUrl, executor); - } - - @NotNull - @Override - public List logs(final LogQuery query) throws ApiException { - final String urlParams = ACT_LOGS_PARAM + query.getParams(); - final LogResponseTO response = getRequest(urlParams, LogResponseTO.class); - BasicUtils.validateTxResponse(response); - - return (BasicUtils.isEmpty(response.getResult())) - ? Collections.emptyList() - : response.getResult(); - } -} diff --git a/src/main/java/io/api/etherscan/error/ApiException.java b/src/main/java/io/api/etherscan/error/ApiException.java deleted file mode 100644 index 33e4228..0000000 --- a/src/main/java/io/api/etherscan/error/ApiException.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.api.etherscan.error; - -/** - * @author GoodforGod - * @since 30.10.2018 - */ -public class ApiException extends RuntimeException { - - public ApiException(String message) { - super(message); - } - - public ApiException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/main/java/io/api/etherscan/error/ApiKeyException.java b/src/main/java/io/api/etherscan/error/ApiKeyException.java deleted file mode 100644 index 4e22934..0000000 --- a/src/main/java/io/api/etherscan/error/ApiKeyException.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.api.etherscan.error; - -/** - * @author GoodforGod - * @since 05.11.2018 - */ -public class ApiKeyException extends ApiException { - - public ApiKeyException(String message) { - super(message); - } -} diff --git a/src/main/java/io/api/etherscan/error/ApiTimeoutException.java b/src/main/java/io/api/etherscan/error/ApiTimeoutException.java deleted file mode 100644 index 39b6e93..0000000 --- a/src/main/java/io/api/etherscan/error/ApiTimeoutException.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.api.etherscan.error; - -/** - * @author GoodforGod - * @since 12.11.2018 - */ -public class ApiTimeoutException extends ApiException { - - public ApiTimeoutException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/main/java/io/api/etherscan/error/ConnectionException.java b/src/main/java/io/api/etherscan/error/ConnectionException.java deleted file mode 100644 index 96a881c..0000000 --- a/src/main/java/io/api/etherscan/error/ConnectionException.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.api.etherscan.error; - -/** - * @author GoodforGod - * @since 29.10.2018 - */ -public class ConnectionException extends ApiException { - - public ConnectionException(String message) { - super(message); - } - - public ConnectionException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/main/java/io/api/etherscan/error/EtherScanException.java b/src/main/java/io/api/etherscan/error/EtherScanException.java deleted file mode 100644 index cb7dd7f..0000000 --- a/src/main/java/io/api/etherscan/error/EtherScanException.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.api.etherscan.error; - -import io.api.etherscan.model.utility.BaseResponseTO; -import io.api.etherscan.model.utility.StringResponseTO; - -/** - * @author GoodforGod - * @since 29.10.2018 - */ -public class EtherScanException extends ApiException { - - public EtherScanException(BaseResponseTO response) { - this(response.getMessage() + ", with status: " + response.getStatus()); - } - - public EtherScanException(StringResponseTO response) { - this(response.getResult() + ", with status: " + response.getStatus() + ", with message: " + response.getMessage()); - } - - public EtherScanException(String message) { - super(message); - } -} diff --git a/src/main/java/io/api/etherscan/error/EventModelException.java b/src/main/java/io/api/etherscan/error/EventModelException.java deleted file mode 100644 index feb60be..0000000 --- a/src/main/java/io/api/etherscan/error/EventModelException.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.api.etherscan.error; - -public class EventModelException extends ApiException { - - public EventModelException(String message) { - super(message); - } - - public EventModelException(String message, Throwable cause) { - super(message, cause); - } -} diff --git a/src/main/java/io/api/etherscan/error/InvalidAddressException.java b/src/main/java/io/api/etherscan/error/InvalidAddressException.java deleted file mode 100644 index 9a0c143..0000000 --- a/src/main/java/io/api/etherscan/error/InvalidAddressException.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.api.etherscan.error; - -/** - * @author GoodforGod - * @since 29.10.2018 - */ -public class InvalidAddressException extends ApiException { - - public InvalidAddressException(String message) { - super(message); - } -} diff --git a/src/main/java/io/api/etherscan/error/InvalidDataHexException.java b/src/main/java/io/api/etherscan/error/InvalidDataHexException.java deleted file mode 100644 index dd12cb9..0000000 --- a/src/main/java/io/api/etherscan/error/InvalidDataHexException.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.api.etherscan.error; - -/** - * @author GoodforGod - * @since 02.11.2018 - */ -public class InvalidDataHexException extends ApiException { - - public InvalidDataHexException(String message) { - super(message); - } -} diff --git a/src/main/java/io/api/etherscan/error/InvalidTxHashException.java b/src/main/java/io/api/etherscan/error/InvalidTxHashException.java deleted file mode 100644 index aba32c1..0000000 --- a/src/main/java/io/api/etherscan/error/InvalidTxHashException.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.api.etherscan.error; - -/** - * @author GoodforGod - * @since 02.11.2018 - */ -public class InvalidTxHashException extends ApiException { - - public InvalidTxHashException(String message) { - super(message); - } -} diff --git a/src/main/java/io/api/etherscan/error/LogQueryException.java b/src/main/java/io/api/etherscan/error/LogQueryException.java deleted file mode 100644 index 504219f..0000000 --- a/src/main/java/io/api/etherscan/error/LogQueryException.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.api.etherscan.error; - -/** - * @author GoodforGod - * @since 31.10.2018 - */ -public class LogQueryException extends ApiException { - - public LogQueryException(String message) { - super(message); - } -} diff --git a/src/main/java/io/api/etherscan/error/RateLimitException.java b/src/main/java/io/api/etherscan/error/RateLimitException.java deleted file mode 100644 index c29f54d..0000000 --- a/src/main/java/io/api/etherscan/error/RateLimitException.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.api.etherscan.error; - -/** - * @author iSnow - * @since 2020-10-06 - */ -public class RateLimitException extends ApiException { - - public RateLimitException(String message) { - super(message); - } -} diff --git a/src/main/java/io/api/etherscan/manager/IQueueManager.java b/src/main/java/io/api/etherscan/manager/IQueueManager.java deleted file mode 100644 index 98a3172..0000000 --- a/src/main/java/io/api/etherscan/manager/IQueueManager.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.api.etherscan.manager; - -/** - * Queue manager to support API limits (EtherScan 5request\sec limit) Managers grants turn if the - * limit is not exhausted And resets queue each set period - * - * @author GoodforGod - * @since 30.10.2018 - */ -public interface IQueueManager extends AutoCloseable { - - /** - * Waits in queue for chance to take turn - */ - void takeTurn(); -} diff --git a/src/main/java/io/api/etherscan/manager/impl/QueueManager.java b/src/main/java/io/api/etherscan/manager/impl/QueueManager.java deleted file mode 100644 index d3a44de..0000000 --- a/src/main/java/io/api/etherscan/manager/impl/QueueManager.java +++ /dev/null @@ -1,62 +0,0 @@ -package io.api.etherscan.manager.impl; - -import io.api.etherscan.manager.IQueueManager; -import java.util.concurrent.*; - -/** - * Queue Semaphore implementation with size and reset time as params - * - * @see IQueueManager - * @author GoodforGod - * @since 30.10.2018 - */ -public class QueueManager implements IQueueManager, AutoCloseable { - - public static final QueueManager DEFAULT_KEY_QUEUE = new QueueManager(1, 5200L, 5200L, 0); - public static final QueueManager PERSONAL_KEY_QUEUE = new QueueManager(5, 1100L, 1100L, 5); - - private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); - private final Semaphore semaphore; - private final long queueResetTimeInMillis; - - public QueueManager(int size, int resetInSec) { - this(size, resetInSec, resetInSec); - } - - public QueueManager(int size, int queueResetTimeInSec, int delayInSec) { - this(size, queueResetTimeInSec, delayInSec, size); - } - - public QueueManager(int size, int queueResetTimeInSec, int delayInSec, int initialSize) { - this(size, - (long) queueResetTimeInSec * 1000, - (long) delayInSec * 1000, - initialSize); - } - - public QueueManager(int size, long queueResetTimeInMillis, long delayInMillis, int initialSize) { - this.queueResetTimeInMillis = queueResetTimeInMillis; - this.semaphore = new Semaphore(initialSize); - this.executorService.scheduleAtFixedRate(releaseLocks(size), delayInMillis, queueResetTimeInMillis, - TimeUnit.MILLISECONDS); - } - - @SuppressWarnings("java:S899") - @Override - public void takeTurn() { - try { - semaphore.tryAcquire(queueResetTimeInMillis, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - - private Runnable releaseLocks(int toRelease) { - return () -> semaphore.release(toRelease); - } - - @Override - public void close() { - executorService.shutdown(); - } -} diff --git a/src/main/java/io/api/etherscan/model/EthNetwork.java b/src/main/java/io/api/etherscan/model/EthNetwork.java deleted file mode 100644 index 6144cf1..0000000 --- a/src/main/java/io/api/etherscan/model/EthNetwork.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.api.etherscan.model; - -/** - * @author GoodforGod - * @since 28.10.2018 - */ -public enum EthNetwork { - - MAINNET("api"), - ROPSTEN("api-ropsten"), - KOVAN("api-kovan"), - TOBALABA("api-tobalaba"), - GORLI("api-goerli"), - RINKEBY("api-rinkeby"); - - private final String domain; - - EthNetwork(String domain) { - this.domain = domain; - } - - public String getDomain() { - return domain; - } -} diff --git a/src/main/java/io/api/etherscan/model/Uncle.java b/src/main/java/io/api/etherscan/model/Uncle.java deleted file mode 100644 index 7dea648..0000000 --- a/src/main/java/io/api/etherscan/model/Uncle.java +++ /dev/null @@ -1,69 +0,0 @@ -package io.api.etherscan.model; - -import java.math.BigInteger; - -/** - * @author GoodforGod - * @since 30.10.2018 - */ -public class Uncle { - - private String miner; - private BigInteger blockreward; - private int unclePosition; - - // - public String getMiner() { - return miner; - } - - public BigInteger getBlockreward() { - return blockreward; - } - - public int getUnclePosition() { - return unclePosition; - } - // - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - - Uncle uncle = (Uncle) o; - - if (unclePosition != uncle.unclePosition) - return false; - if (miner != null - ? !miner.equals(uncle.miner) - : uncle.miner != null) - return false; - return blockreward != null - ? blockreward.equals(uncle.blockreward) - : uncle.blockreward == null; - } - - @Override - public int hashCode() { - int result = miner != null - ? miner.hashCode() - : 0; - result = 31 * result + (blockreward != null - ? blockreward.hashCode() - : 0); - result = 31 * result + unclePosition; - return result; - } - - @Override - public String toString() { - return "Uncle{" + - "miner='" + miner + '\'' + - ", blockreward=" + blockreward + - ", unclePosition=" + unclePosition + - '}'; - } -} diff --git a/src/main/java/io/api/etherscan/model/UncleBlock.java b/src/main/java/io/api/etherscan/model/UncleBlock.java deleted file mode 100644 index ff30451..0000000 --- a/src/main/java/io/api/etherscan/model/UncleBlock.java +++ /dev/null @@ -1,65 +0,0 @@ -package io.api.etherscan.model; - -import io.api.etherscan.util.BasicUtils; -import java.util.List; - -/** - * @author GoodforGod - * @since 30.10.2018 - */ -public class UncleBlock extends Block { - - private String blockMiner; - private List uncles; - private String uncleInclusionReward; - - // - public boolean isEmpty() { - return getBlockNumber() == 0 && getBlockReward() == null - && getTimeStamp() == null - && BasicUtils.isEmpty(blockMiner); - } - - public String getBlockMiner() { - return blockMiner; - } - - public List getUncles() { - return uncles; - } - - public String getUncleInclusionReward() { - return uncleInclusionReward; - } - // - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - if (!super.equals(o)) - return false; - - UncleBlock that = (UncleBlock) o; - - return getBlockNumber() != 0 && getBlockNumber() == that.getBlockNumber(); - } - - @Override - public int hashCode() { - int result = super.hashCode(); - result = (int) (31 * result + getBlockNumber()); - return result; - } - - @Override - public String toString() { - return "UncleBlock{" + - "blockMiner='" + blockMiner + '\'' + - ", uncles=" + uncles + - ", uncleInclusionReward='" + uncleInclusionReward + '\'' + - '}'; - } -} diff --git a/src/main/java/io/api/etherscan/model/query/IQueryBuilder.java b/src/main/java/io/api/etherscan/model/query/IQueryBuilder.java deleted file mode 100644 index 6a76c62..0000000 --- a/src/main/java/io/api/etherscan/model/query/IQueryBuilder.java +++ /dev/null @@ -1,15 +0,0 @@ -package io.api.etherscan.model.query; - -import io.api.etherscan.error.LogQueryException; -import io.api.etherscan.model.query.impl.LogQuery; - -/** - * Builder, part of The Event Log API - * - * @author GoodforGod - * @since 31.10.2018 - */ -public interface IQueryBuilder { - - LogQuery build() throws LogQueryException; -} diff --git a/src/main/java/io/api/etherscan/model/query/impl/LogQuery.java b/src/main/java/io/api/etherscan/model/query/impl/LogQuery.java deleted file mode 100644 index 31d8c13..0000000 --- a/src/main/java/io/api/etherscan/model/query/impl/LogQuery.java +++ /dev/null @@ -1,28 +0,0 @@ -package io.api.etherscan.model.query.impl; - -import io.api.etherscan.core.ILogsApi; - -/** - * Final builded container for The Event Log API - * EtherScan - API Descriptions https://etherscan.io/apis#logs - * - * @see LogQueryBuilder - * @see ILogsApi - * @author GoodforGod - * @since 31.10.2018 - */ -public class LogQuery { - - /** - * Final request parameter for api call - */ - private final String params; - - LogQuery(String params) { - this.params = params; - } - - public String getParams() { - return params; - } -} diff --git a/src/main/java/io/api/etherscan/model/query/impl/LogQueryBuilder.java b/src/main/java/io/api/etherscan/model/query/impl/LogQueryBuilder.java deleted file mode 100644 index 44ca825..0000000 --- a/src/main/java/io/api/etherscan/model/query/impl/LogQueryBuilder.java +++ /dev/null @@ -1,83 +0,0 @@ -package io.api.etherscan.model.query.impl; - -import io.api.etherscan.core.ILogsApi; -import io.api.etherscan.error.LogQueryException; -import io.api.etherscan.model.query.IQueryBuilder; -import io.api.etherscan.util.BasicUtils; - -/** - * Builder for The Event Log API - * - * @see ILogsApi - * @author GoodforGod - * @since 31.10.2018 - */ -public class LogQueryBuilder implements IQueryBuilder { - - private static final long MIN_BLOCK = 0; - private static final long MAX_BLOCK = 99999999999L; - - private final String address; - private final long startBlock, endBlock; - - private LogQueryBuilder(String address, long startBlock, long endBlock) { - this.address = address; - this.startBlock = startBlock; - this.endBlock = endBlock; - } - - public static LogQueryBuilder with(String address) { - return with(address, MIN_BLOCK); - } - - public static LogQueryBuilder with(String address, long startBlock) { - return with(address, startBlock, MAX_BLOCK); - } - - public static LogQueryBuilder with(String address, long startBlock, long endBlock) { - BasicUtils.validateAddress(address); - return new LogQueryBuilder(address, startBlock, endBlock); - } - - public LogTopicSingle topic(String topic0) { - if (BasicUtils.isNotHex(topic0)) - throw new LogQueryException("topic0 can not be empty or non hex."); - return new LogTopicSingle(address, startBlock, endBlock, topic0); - } - - public LogTopicTuple topic(String topic0, String topic1) { - if (BasicUtils.isNotHex(topic0)) - throw new LogQueryException("topic0 can not be empty or non hex."); - if (BasicUtils.isNotHex(topic1)) - throw new LogQueryException("topic1 can not be empty or non hex."); - return new LogTopicTuple(address, startBlock, endBlock, topic0, topic1); - } - - public LogTopicTriple topic(String topic0, String topic1, String topic2) { - if (BasicUtils.isNotHex(topic0)) - throw new LogQueryException("topic0 can not be empty or non hex."); - if (BasicUtils.isNotHex(topic1)) - throw new LogQueryException("topic1 can not be empty or non hex."); - if (BasicUtils.isNotHex(topic2)) - throw new LogQueryException("topic2 can not be empty or non hex."); - return new LogTopicTriple(address, startBlock, endBlock, topic0, topic1, topic2); - } - - public LogTopicQuadro topic(String topic0, String topic1, String topic2, String topic3) { - if (BasicUtils.isNotHex(topic0)) - throw new LogQueryException("topic0 can not be empty or non hex."); - if (BasicUtils.isNotHex(topic1)) - throw new LogQueryException("topic1 can not be empty or non hex."); - if (BasicUtils.isNotHex(topic2)) - throw new LogQueryException("topic2 can not be empty or non hex."); - if (BasicUtils.isNotHex(topic3)) - throw new LogQueryException("topic3 can not be empty or non hex."); - - return new LogTopicQuadro(address, startBlock, endBlock, topic0, topic1, topic2, topic3); - } - - @Override - public LogQuery build() throws LogQueryException { - return new LogQuery("&address=" + this.address + "&fromBlock=" + this.startBlock + "&toBlock=" + this.endBlock); - } -} diff --git a/src/main/java/io/api/etherscan/model/utility/TxTokenResponseTO.java b/src/main/java/io/api/etherscan/model/utility/TxTokenResponseTO.java deleted file mode 100644 index 1cbd4e3..0000000 --- a/src/main/java/io/api/etherscan/model/utility/TxTokenResponseTO.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.api.etherscan.model.utility; - -import io.api.etherscan.model.TxToken; - -/** - * @author GoodforGod - * @since 29.10.2018 - */ -public class TxTokenResponseTO extends BaseListResponseTO { - -} diff --git a/src/main/java/io/api/etherscan/model/utility/UncleBlockResponseTO.java b/src/main/java/io/api/etherscan/model/utility/UncleBlockResponseTO.java deleted file mode 100644 index f8e4c5e..0000000 --- a/src/main/java/io/api/etherscan/model/utility/UncleBlockResponseTO.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.api.etherscan.model.utility; - -import io.api.etherscan.model.UncleBlock; - -/** - * @author GoodforGod - * @since 30.10.2018 - */ -public class UncleBlockResponseTO extends BaseResponseTO { - - private UncleBlock result; - - public UncleBlock getResult() { - return result; - } -} diff --git a/src/main/java/io/api/etherscan/core/IAccountApi.java b/src/main/java/io/goodforgod/api/etherscan/AccountAPI.java similarity index 55% rename from src/main/java/io/api/etherscan/core/IAccountApi.java rename to src/main/java/io/goodforgod/api/etherscan/AccountAPI.java index ee869a2..7a0df39 100644 --- a/src/main/java/io/api/etherscan/core/IAccountApi.java +++ b/src/main/java/io/goodforgod/api/etherscan/AccountAPI.java @@ -1,27 +1,27 @@ -package io.api.etherscan.core; +package io.goodforgod.api.etherscan; -import io.api.etherscan.error.ApiException; -import io.api.etherscan.model.*; +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.model.*; import java.util.List; import org.jetbrains.annotations.NotNull; /** - * EtherScan - API Descriptions https://etherscan.io/apis#accounts + * EtherScan - API Descriptions ... * * @author GoodforGod * @since 28.10.2018 */ -public interface IAccountApi { +public interface AccountAPI { /** * Address ETH balance * * @param address get balance for * @return balance - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - Balance balance(String address) throws ApiException; + Balance balance(String address) throws EtherScanException; /** * ERC20 token balance for address @@ -29,10 +29,10 @@ public interface IAccountApi { * @param address get balance for * @param contract token contract * @return token balance for address - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - TokenBalance balance(String address, String contract) throws ApiException; + TokenBalance balance(String address, String contract) throws EtherScanException; /** * Maximum 20 address for single batch request If address MORE THAN 20, then there will be more than @@ -40,10 +40,10 @@ public interface IAccountApi { * * @param addresses addresses to get balances for * @return list of balances - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - List balances(List addresses) throws ApiException; + List balances(List addresses) throws EtherScanException; /** * All txs for given address @@ -52,16 +52,16 @@ public interface IAccountApi { * @param startBlock tx from this blockNumber * @param endBlock tx to this blockNumber * @return txs for address - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - List txs(String address, long startBlock, long endBlock) throws ApiException; + List txs(String address, long startBlock, long endBlock) throws EtherScanException; @NotNull - List txs(String address, long startBlock) throws ApiException; + List txs(String address, long startBlock) throws EtherScanException; @NotNull - List txs(String address) throws ApiException; + List txs(String address) throws EtherScanException; /** * All internal txs for given address @@ -70,26 +70,26 @@ public interface IAccountApi { * @param startBlock tx from this blockNumber * @param endBlock tx to this blockNumber * @return txs for address - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - List txsInternal(String address, long startBlock, long endBlock) throws ApiException; + List txsInternal(String address, long startBlock, long endBlock) throws EtherScanException; @NotNull - List txsInternal(String address, long startBlock) throws ApiException; + List txsInternal(String address, long startBlock) throws EtherScanException; @NotNull - List txsInternal(String address) throws ApiException; + List txsInternal(String address) throws EtherScanException; /** * All internal tx for given transaction hash * * @param txhash transaction hash * @return internal txs list - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - List txsInternalByHash(String txhash) throws ApiException; + List txsInternalByHash(String txhash) throws EtherScanException; /** * All ERC-20 token txs for given address @@ -98,16 +98,16 @@ public interface IAccountApi { * @param startBlock tx from this blockNumber * @param endBlock tx to this blockNumber * @return txs for address - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - List txsToken(String address, long startBlock, long endBlock) throws ApiException; + List txsERC20(String address, long startBlock, long endBlock) throws EtherScanException; @NotNull - List txsToken(String address, long startBlock) throws ApiException; + List txsERC20(String address, long startBlock) throws EtherScanException; @NotNull - List txsToken(String address) throws ApiException; + List txsERC20(String address) throws EtherScanException; /** * All ERC-721 (NFT) token txs for given address @@ -116,24 +116,24 @@ public interface IAccountApi { * @param startBlock tx from this blockNumber * @param endBlock tx to this blockNumber * @return txs for address - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - List txsNftToken(String address, long startBlock, long endBlock) throws ApiException; + List txsERC721(String address, long startBlock, long endBlock) throws EtherScanException; @NotNull - List txsNftToken(String address, long startBlock) throws ApiException; + List txsERC721(String address, long startBlock) throws EtherScanException; @NotNull - List txsNftToken(String address) throws ApiException; + List txsERC721(String address) throws EtherScanException; /** * All blocks mined by address * * @param address address to search for * @return blocks mined - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - List minedBlocks(String address) throws ApiException; + List blocksMined(String address) throws EtherScanException; } diff --git a/src/main/java/io/api/etherscan/core/impl/AccountApiProvider.java b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java similarity index 59% rename from src/main/java/io/api/etherscan/core/impl/AccountApiProvider.java rename to src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java index c807598..7cc5f52 100644 --- a/src/main/java/io/api/etherscan/core/impl/AccountApiProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java @@ -1,13 +1,12 @@ -package io.api.etherscan.core.impl; - -import io.api.etherscan.core.IAccountApi; -import io.api.etherscan.error.ApiException; -import io.api.etherscan.error.EtherScanException; -import io.api.etherscan.executor.IHttpExecutor; -import io.api.etherscan.manager.IQueueManager; -import io.api.etherscan.model.*; -import io.api.etherscan.model.utility.*; -import io.api.etherscan.util.BasicUtils; +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.error.EtherScanResponseException; +import io.goodforgod.api.etherscan.executor.EthHttpClient; +import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import io.goodforgod.api.etherscan.model.*; +import io.goodforgod.api.etherscan.model.response.*; +import io.goodforgod.api.etherscan.util.BasicUtils; import java.math.BigInteger; import java.util.ArrayList; import java.util.Collections; @@ -18,11 +17,11 @@ /** * Account API Implementation * - * @see IAccountApi + * @see AccountAPI * @author GoodforGod * @since 28.10.2018 */ -public class AccountApiProvider extends BasicProvider implements IAccountApi { +final class AccountAPIProvider extends BasicProvider implements AccountAPI { private static final int OFFSET_MAX = 10000; @@ -47,42 +46,42 @@ public class AccountApiProvider extends BasicProvider implements IAccountApi { private static final String OFFSET_PARAM = "&offset="; private static final String PAGE_PARAM = "&page="; - AccountApiProvider(final IQueueManager queueManager, - final String baseUrl, - final IHttpExecutor executor) { - super(queueManager, "account", baseUrl, executor); + AccountAPIProvider(RequestQueueManager requestQueueManager, + String baseUrl, + EthHttpClient executor) { + super(requestQueueManager, "account", baseUrl, executor); } @NotNull @Override - public Balance balance(final String address) throws ApiException { + public Balance balance(String address) throws EtherScanException { BasicUtils.validateAddress(address); final String urlParams = ACT_BALANCE_ACTION + TAG_LATEST_PARAM + ADDRESS_PARAM + address; final StringResponseTO response = getRequest(urlParams, StringResponseTO.class); if (response.getStatus() != 1) - throw new EtherScanException(response); + throw new EtherScanResponseException(response); return new Balance(address, new BigInteger(response.getResult())); } @NotNull @Override - public TokenBalance balance(final String address, final String contract) throws ApiException { + public TokenBalance balance(String address, String contract) throws EtherScanException { BasicUtils.validateAddress(address); BasicUtils.validateAddress(contract); final String urlParams = ACT_TOKEN_BALANCE_PARAM + ADDRESS_PARAM + address + CONTRACT_PARAM + contract; final StringResponseTO response = getRequest(urlParams, StringResponseTO.class); if (response.getStatus() != 1) - throw new EtherScanException(response); + throw new EtherScanResponseException(response); return new TokenBalance(address, new BigInteger(response.getResult()), contract); } @NotNull @Override - public List balances(final List addresses) throws ApiException { + public List balances(List addresses) throws EtherScanException { if (BasicUtils.isEmpty(addresses)) return Collections.emptyList(); @@ -96,7 +95,7 @@ public List balances(final List addresses) throws ApiException final String urlParams = ACT_BALANCE_MULTI_ACTION + TAG_LATEST_PARAM + ADDRESS_PARAM + toAddressParam(batch); final BalanceResponseTO response = getRequest(urlParams, BalanceResponseTO.class); if (response.getStatus() != 1) - throw new EtherScanException(response); + throw new EtherScanResponseException(response); if (!BasicUtils.isEmpty(response.getResult())) balances.addAll(response.getResult().stream() @@ -107,31 +106,32 @@ public List balances(final List addresses) throws ApiException return balances; } - private String toAddressParam(final List addresses) { - return addresses.stream().collect(Collectors.joining(",")); + private String toAddressParam(List addresses) { + return String.join(",", addresses); } @NotNull @Override - public List txs(final String address) throws ApiException { + public List txs(String address) throws EtherScanException { return txs(address, MIN_START_BLOCK); } @NotNull @Override - public List txs(final String address, final long startBlock) throws ApiException { + public List txs(String address, long startBlock) throws EtherScanException { return txs(address, startBlock, MAX_END_BLOCK); } @NotNull @Override - public List txs(final String address, final long startBlock, final long endBlock) throws ApiException { + public List txs(String address, long startBlock, long endBlock) throws EtherScanException { BasicUtils.validateAddress(address); final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); - final String offsetParam = PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX; - final String blockParam = START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end(); - final String urlParams = ACT_TX_ACTION + offsetParam + ADDRESS_PARAM + address + blockParam + SORT_ASC_PARAM; + final String urlParams = ACT_TX_ACTION + PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX + + ADDRESS_PARAM + address + + START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end() + + SORT_ASC_PARAM; return getRequestUsingOffset(urlParams, TxResponseTO.class); } @@ -147,7 +147,7 @@ public List txs(final String address, final long startBlock, final long endB */ private List getRequestUsingOffset(final String urlParams, Class tClass) - throws ApiException { + throws EtherScanException { final List result = new ArrayList<>(); int page = 1; while (true) { @@ -167,32 +167,34 @@ private List getRequestUsingOffset(final St @NotNull @Override - public List txsInternal(final String address) throws ApiException { + public List txsInternal(String address) throws EtherScanException { return txsInternal(address, MIN_START_BLOCK); } @NotNull @Override - public List txsInternal(final String address, final long startBlock) throws ApiException { + public List txsInternal(String address, long startBlock) throws EtherScanException { return txsInternal(address, startBlock, MAX_END_BLOCK); } @NotNull @Override - public List txsInternal(final String address, final long startBlock, final long endBlock) throws ApiException { + public List txsInternal(String address, long startBlock, long endBlock) + throws EtherScanException { BasicUtils.validateAddress(address); final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); - final String offsetParam = PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX; - final String blockParam = START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end(); - final String urlParams = ACT_TX_INTERNAL_ACTION + offsetParam + ADDRESS_PARAM + address + blockParam + SORT_ASC_PARAM; + final String urlParams = ACT_TX_INTERNAL_ACTION + PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX + + ADDRESS_PARAM + address + + START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end() + + SORT_ASC_PARAM; return getRequestUsingOffset(urlParams, TxInternalResponseTO.class); } @NotNull @Override - public List txsInternalByHash(final String txhash) throws ApiException { + public List txsInternalByHash(String txhash) throws EtherScanException { BasicUtils.validateTxHash(txhash); final String urlParams = ACT_TX_INTERNAL_ACTION + TXHASH_PARAM + txhash; @@ -206,61 +208,63 @@ public List txsInternalByHash(final String txhash) throws ApiExcepti @NotNull @Override - public List txsToken(final String address) throws ApiException { - return txsToken(address, MIN_START_BLOCK); + public List txsERC20(String address) throws EtherScanException { + return txsERC20(address, MIN_START_BLOCK); } @NotNull @Override - public List txsToken(final String address, final long startBlock) throws ApiException { - return txsToken(address, startBlock, MAX_END_BLOCK); + public List txsERC20(String address, long startBlock) throws EtherScanException { + return txsERC20(address, startBlock, MAX_END_BLOCK); } @NotNull @Override - public List txsToken(final String address, final long startBlock, final long endBlock) throws ApiException { + public List txsERC20(String address, long startBlock, long endBlock) throws EtherScanException { BasicUtils.validateAddress(address); final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); - final String offsetParam = PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX; - final String blockParam = START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end(); - final String urlParams = ACT_TX_TOKEN_ACTION + offsetParam + ADDRESS_PARAM + address + blockParam + SORT_ASC_PARAM; + final String urlParams = ACT_TX_TOKEN_ACTION + PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX + + ADDRESS_PARAM + address + + START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end() + + SORT_ASC_PARAM; - return getRequestUsingOffset(urlParams, TxTokenResponseTO.class); + return getRequestUsingOffset(urlParams, TxERC20ResponseTO.class); } @NotNull @Override - public List txsNftToken(String address) throws ApiException { - return txsNftToken(address, MIN_START_BLOCK); + public List txsERC721(String address) throws EtherScanException { + return txsERC721(address, MIN_START_BLOCK); } @NotNull @Override - public List txsNftToken(String address, long startBlock) throws ApiException { - return txsNftToken(address, startBlock, MAX_END_BLOCK); + public List txsERC721(String address, long startBlock) throws EtherScanException { + return txsERC721(address, startBlock, MAX_END_BLOCK); } @NotNull @Override - public List txsNftToken(String address, long startBlock, long endBlock) throws ApiException { + public List txsERC721(String address, long startBlock, long endBlock) throws EtherScanException { BasicUtils.validateAddress(address); final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); - final String offsetParam = PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX; - final String blockParam = START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end(); - final String urlParams = ACT_TX_NFT_TOKEN_ACTION + offsetParam + ADDRESS_PARAM + address + blockParam + SORT_ASC_PARAM; + final String urlParams = ACT_TX_NFT_TOKEN_ACTION + PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX + + ADDRESS_PARAM + address + + START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end() + + SORT_ASC_PARAM; - return getRequestUsingOffset(urlParams, TxTokenResponseTO.class); + return getRequestUsingOffset(urlParams, TxERC20ResponseTO.class); } @NotNull @Override - public List minedBlocks(final String address) throws ApiException { + public List blocksMined(String address) throws EtherScanException { BasicUtils.validateAddress(address); - final String offsetParam = PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX; - final String urlParams = ACT_MINED_ACTION + offsetParam + BLOCK_TYPE_PARAM + ADDRESS_PARAM + address; + final String urlParams = ACT_MINED_ACTION + PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX + BLOCK_TYPE_PARAM + + ADDRESS_PARAM + address; return getRequestUsingOffset(urlParams, BlockResponseTO.class); } diff --git a/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java b/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java new file mode 100644 index 0000000..4fd625a --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java @@ -0,0 +1,93 @@ +package io.goodforgod.api.etherscan; + +import com.google.gson.*; +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.error.EtherScanParseException; +import io.goodforgod.api.etherscan.error.EtherScanRateLimitException; +import io.goodforgod.api.etherscan.error.EtherScanResponseException; +import io.goodforgod.api.etherscan.executor.EthHttpClient; +import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import io.goodforgod.api.etherscan.model.response.StringResponseTO; +import io.goodforgod.api.etherscan.util.BasicUtils; +import io.goodforgod.gson.configuration.GsonConfiguration; +import java.util.Map; + +/** + * Base provider for API Implementations + * + * @author GoodforGod + * @see EtherScanAPIProvider + * @since 28.10.2018 + */ +abstract class BasicProvider { + + static final int MAX_END_BLOCK = Integer.MAX_VALUE; + static final int MIN_START_BLOCK = 0; + + static final String ACT_PREFIX = "&action="; + + private final String module; + private final String baseUrl; + private final EthHttpClient executor; + private final RequestQueueManager queue; + private final Gson gson; + + BasicProvider(RequestQueueManager queue, + String module, + String baseUrl, + EthHttpClient executor) { + this.queue = queue; + this.module = "&module=" + module; + this.baseUrl = baseUrl; + this.executor = executor; + this.gson = new GsonConfiguration().builder().create(); + } + + T convert(String json, Class tClass) { + try { + final T t = gson.fromJson(json, tClass); + if (t instanceof StringResponseTO && ((StringResponseTO) t).getResult().startsWith("Max rate limit reached")) { + throw new EtherScanRateLimitException(((StringResponseTO) t).getResult()); + } + + return t; + } catch (Exception e) { + try { + final Map map = gson.fromJson(json, Map.class); + final Object result = map.get("result"); + if (result instanceof String && ((String) result).startsWith("Max rate limit reached")) + throw new EtherScanRateLimitException(((String) result)); + + throw new EtherScanParseException(e.getMessage() + ", for response: " + json, e.getCause(), json); + } catch (EtherScanException ex) { + throw ex; + } catch (Exception ex) { + throw new EtherScanParseException(e.getMessage() + ", for response: " + json, e.getCause(), json); + } + } + } + + String getRequest(String urlParameters) { + queue.takeTurn(); + final String url = baseUrl + module + urlParameters; + final String result = executor.get(url); + if (BasicUtils.isEmpty(result)) + throw new EtherScanResponseException("Server returned null value for GET request at URL - " + url); + + return result; + } + + String postRequest(String urlParameters, String dataToPost) { + queue.takeTurn(); + final String url = baseUrl + module + urlParameters; + return executor.post(url, dataToPost); + } + + T getRequest(String urlParameters, Class tClass) { + return convert(getRequest(urlParameters), tClass); + } + + T postRequest(String urlParameters, String dataToPost, Class tClass) { + return convert(postRequest(urlParameters, dataToPost), tClass); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/BlockAPI.java b/src/main/java/io/goodforgod/api/etherscan/BlockAPI.java new file mode 100644 index 0000000..55a8c3b --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/BlockAPI.java @@ -0,0 +1,25 @@ +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.model.BlockUncle; +import java.util.Optional; +import org.jetbrains.annotations.NotNull; + +/** + * EtherScan - API Descriptions ... + * + * @author GoodforGod + * @since 30.10.2018 + */ +public interface BlockAPI { + + /** + * Return uncle blocks + * + * @param blockNumber block number form 0 to last + * @return optional uncle blocks + * @throws EtherScanException parent exception class + */ + @NotNull + Optional uncles(long blockNumber) throws EtherScanException; +} diff --git a/src/main/java/io/api/etherscan/core/impl/BlockApiProvider.java b/src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java similarity index 53% rename from src/main/java/io/api/etherscan/core/impl/BlockApiProvider.java rename to src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java index d634c9b..e5a6d49 100644 --- a/src/main/java/io/api/etherscan/core/impl/BlockApiProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java @@ -1,37 +1,36 @@ -package io.api.etherscan.core.impl; +package io.goodforgod.api.etherscan; -import io.api.etherscan.core.IBlockApi; -import io.api.etherscan.error.ApiException; -import io.api.etherscan.executor.IHttpExecutor; -import io.api.etherscan.manager.IQueueManager; -import io.api.etherscan.model.UncleBlock; -import io.api.etherscan.model.utility.UncleBlockResponseTO; -import io.api.etherscan.util.BasicUtils; +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.executor.EthHttpClient; +import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import io.goodforgod.api.etherscan.model.BlockUncle; +import io.goodforgod.api.etherscan.model.response.UncleBlockResponseTO; +import io.goodforgod.api.etherscan.util.BasicUtils; import java.util.Optional; import org.jetbrains.annotations.NotNull; /** * Block API Implementation * - * @see IBlockApi + * @see BlockAPI * @author GoodforGod * @since 28.10.2018 */ -public class BlockApiProvider extends BasicProvider implements IBlockApi { +final class BlockAPIProvider extends BasicProvider implements BlockAPI { private static final String ACT_BLOCK_PARAM = ACT_PREFIX + "getblockreward"; private static final String BLOCKNO_PARAM = "&blockno="; - BlockApiProvider(final IQueueManager queueManager, - final String baseUrl, - final IHttpExecutor executor) { - super(queueManager, "block", baseUrl, executor); + BlockAPIProvider(RequestQueueManager requestQueueManager, + String baseUrl, + EthHttpClient executor) { + super(requestQueueManager, "block", baseUrl, executor); } @NotNull @Override - public Optional uncles(long blockNumber) throws ApiException { + public Optional uncles(long blockNumber) throws EtherScanException { final String urlParam = ACT_BLOCK_PARAM + BLOCKNO_PARAM + blockNumber; final String response = getRequest(urlParam); if (BasicUtils.isEmpty(response) || response.contains("NOTOK")) diff --git a/src/main/java/io/goodforgod/api/etherscan/ContractAPI.java b/src/main/java/io/goodforgod/api/etherscan/ContractAPI.java new file mode 100644 index 0000000..9271347 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/ContractAPI.java @@ -0,0 +1,24 @@ +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.model.Abi; +import org.jetbrains.annotations.NotNull; + +/** + * EtherScan - API Descriptions ... + * + * @author GoodforGod + * @since 28.10.2018 + */ +public interface ContractAPI { + + /** + * Get Verified Contract Sources + * + * @param address to verify + * @return ABI verified + * @throws EtherScanException parent exception class + */ + @NotNull + Abi contractAbi(String address) throws EtherScanException; +} diff --git a/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java new file mode 100644 index 0000000..cd96f68 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java @@ -0,0 +1,45 @@ +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.error.EtherScanResponseException; +import io.goodforgod.api.etherscan.executor.EthHttpClient; +import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import io.goodforgod.api.etherscan.model.Abi; +import io.goodforgod.api.etherscan.model.response.StringResponseTO; +import io.goodforgod.api.etherscan.util.BasicUtils; +import org.jetbrains.annotations.NotNull; + +/** + * Contract API Implementation + * + * @see ContractAPI + * @author GoodforGod + * @since 28.10.2018 + */ +final class ContractAPIProvider extends BasicProvider implements ContractAPI { + + private static final String ACT_ABI_PARAM = ACT_PREFIX + "getabi"; + + private static final String ADDRESS_PARAM = "&address="; + + ContractAPIProvider(RequestQueueManager requestQueueManager, + String baseUrl, + EthHttpClient executor) { + super(requestQueueManager, "contract", baseUrl, executor); + } + + @NotNull + @Override + public Abi contractAbi(String address) throws EtherScanException { + BasicUtils.validateAddress(address); + + final String urlParam = ACT_ABI_PARAM + ADDRESS_PARAM + address; + final StringResponseTO response = getRequest(urlParam, StringResponseTO.class); + if (response.getStatus() != 1 && "NOTOK".equals(response.getMessage())) + throw new EtherScanResponseException(response); + + return (response.getResult().startsWith("Contract sou")) + ? Abi.nonVerified() + : Abi.verified(response.getResult()); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/EthNetwork.java b/src/main/java/io/goodforgod/api/etherscan/EthNetwork.java new file mode 100644 index 0000000..ce0d929 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/EthNetwork.java @@ -0,0 +1,14 @@ +package io.goodforgod.api.etherscan; + +import java.net.URI; +import org.jetbrains.annotations.NotNull; + +/** + * @author Anton Kurako (GoodforGod) + * @since 11.05.2023 + */ +public interface EthNetwork { + + @NotNull + URI domain(); +} diff --git a/src/main/java/io/goodforgod/api/etherscan/EthNetworks.java b/src/main/java/io/goodforgod/api/etherscan/EthNetworks.java new file mode 100644 index 0000000..4dbe138 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/EthNetworks.java @@ -0,0 +1,29 @@ +package io.goodforgod.api.etherscan; + +import java.net.URI; +import org.jetbrains.annotations.NotNull; + +/** + * @author GoodforGod + * @since 28.10.2018 + */ +public enum EthNetworks implements EthNetwork { + + MAINNET("api", "io"), + ROPSTEN("api-ropsten", "io"), + KOVAN("api-kovan", "io"), + TOBALABA("api-tobalaba", "com"), + GORLI("api-goerli", "io"), + RINKEBY("api-rinkeby", "io"); + + private final URI domain; + + EthNetworks(String domain, String extension) { + this.domain = URI.create("https://" + domain + ".etherscan." + extension + "/api"); + } + + @Override + public @NotNull URI domain() { + return domain; + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java b/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java new file mode 100644 index 0000000..d36d385 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java @@ -0,0 +1,71 @@ +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.error.EtherScanKeyException; +import io.goodforgod.api.etherscan.executor.EthHttpClient; +import io.goodforgod.api.etherscan.executor.impl.UrlEthHttpClient; +import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import io.goodforgod.api.etherscan.manager.impl.FakeRequestQueueManager; +import io.goodforgod.api.etherscan.util.BasicUtils; +import java.util.function.Supplier; +import org.jetbrains.annotations.NotNull; + +/** + * @author Anton Kurako (GoodforGod) + * @since 11.05.2023 + */ +final class EthScanAPIBuilder implements EtherScanAPI.Builder { + + private static final Supplier DEFAULT_SUPPLIER = UrlEthHttpClient::new; + private static final String DEFAULT_KEY = "YourApiKeyToken"; + + private String apiKey = DEFAULT_KEY; + private EthNetwork ethNetwork = EthNetworks.MAINNET; + private RequestQueueManager queueManager = RequestQueueManager.DEFAULT_KEY_QUEUE; + private Supplier ethHttpClientSupplier = DEFAULT_SUPPLIER; + + @NotNull + @Override + public EtherScanAPI.Builder withApiKey(@NotNull String apiKey) { + if (BasicUtils.isBlank(apiKey)) + throw new EtherScanKeyException("API key can not be null or empty"); + + this.apiKey = apiKey; + if (!DEFAULT_KEY.equals(apiKey)) { + queueManager = new FakeRequestQueueManager(); + } + return this; + } + + @NotNull + @Override + public EtherScanAPI.Builder withNetwork(@NotNull EthNetwork network) { + this.ethNetwork = network; + return this; + } + + @NotNull + @Override + public EtherScanAPI.Builder withNetwork(@NotNull EthNetworks network) { + this.ethNetwork = network; + return this; + } + + @NotNull + @Override + public EtherScanAPI.Builder withQueue(@NotNull RequestQueueManager queueManager) { + this.queueManager = queueManager; + return this; + } + + @NotNull + @Override + public EtherScanAPI.Builder withHttpClient(@NotNull Supplier httpClientSupplier) { + this.ethHttpClientSupplier = httpClientSupplier; + return this; + } + + @Override + public @NotNull EtherScanAPI build() { + return new EtherScanAPIProvider(apiKey, ethNetwork, ethHttpClientSupplier, queueManager); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java new file mode 100644 index 0000000..76dcab7 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java @@ -0,0 +1,61 @@ +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.executor.EthHttpClient; +import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import java.util.function.Supplier; +import org.jetbrains.annotations.NotNull; + +/** + * EtherScan full API Description ... + * + * @author GoodforGod + * @since 10.05.2023 + */ +public interface EtherScanAPI extends AutoCloseable { + + @NotNull + AccountAPI account(); + + @NotNull + ContractAPI contract(); + + @NotNull + TransactionAPI txs(); + + @NotNull + BlockAPI block(); + + @NotNull + LogsAPI logs(); + + @NotNull + ProxyAPI proxy(); + + @NotNull + StatisticAPI stats(); + + static Builder builder() { + return new EthScanAPIBuilder(); + } + + interface Builder { + + @NotNull + Builder withApiKey(@NotNull String apiKey); + + @NotNull + Builder withNetwork(@NotNull EthNetwork network); + + @NotNull + Builder withNetwork(@NotNull EthNetworks network); + + @NotNull + Builder withQueue(@NotNull RequestQueueManager queueManager); + + @NotNull + Builder withHttpClient(@NotNull Supplier httpClientSupplier); + + @NotNull + EtherScanAPI build(); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/EtherScanAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPIProvider.java new file mode 100644 index 0000000..0043e37 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPIProvider.java @@ -0,0 +1,89 @@ +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.executor.EthHttpClient; +import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import java.util.function.Supplier; +import org.jetbrains.annotations.NotNull; + +/** + * EtherScan full API Description ... + * + * @author GoodforGod + * @since 28.10.2018 + */ +final class EtherScanAPIProvider implements EtherScanAPI { + + private final RequestQueueManager requestQueueManager; + private final AccountAPI account; + private final BlockAPI block; + private final ContractAPI contract; + private final LogsAPI logs; + private final ProxyAPI proxy; + private final StatisticAPI stats; + private final TransactionAPI txs; + + EtherScanAPIProvider(String apiKey, + EthNetwork network, + Supplier executorSupplier, + RequestQueueManager queue) { + // EtherScan 1request\5sec limit support by queue manager + final EthHttpClient executor = executorSupplier.get(); + final String baseUrl = network.domain() + "?apikey=" + apiKey; + + this.requestQueueManager = queue; + this.account = new AccountAPIProvider(queue, baseUrl, executor); + this.block = new BlockAPIProvider(queue, baseUrl, executor); + this.contract = new ContractAPIProvider(queue, baseUrl, executor); + this.logs = new LogsAPIProvider(queue, baseUrl, executor); + this.proxy = new ProxyAPIProvider(queue, baseUrl, executor); + this.stats = new StatisticAPIProvider(queue, baseUrl, executor); + this.txs = new TransactionAPIProvider(queue, baseUrl, executor); + } + + @NotNull + @Override + public AccountAPI account() { + return account; + } + + @NotNull + @Override + public ContractAPI contract() { + return contract; + } + + @NotNull + @Override + public TransactionAPI txs() { + return txs; + } + + @NotNull + @Override + public BlockAPI block() { + return block; + } + + @NotNull + @Override + public LogsAPI logs() { + return logs; + } + + @NotNull + @Override + public ProxyAPI proxy() { + return proxy; + } + + @NotNull + @Override + public StatisticAPI stats() { + return stats; + } + + @Override + public void close() throws Exception { + requestQueueManager.close(); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/LogsAPI.java b/src/main/java/io/goodforgod/api/etherscan/LogsAPI.java new file mode 100644 index 0000000..5b834df --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/LogsAPI.java @@ -0,0 +1,27 @@ +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.model.Log; +import io.goodforgod.api.etherscan.model.query.LogQuery; +import java.util.List; +import org.jetbrains.annotations.NotNull; + +/** + * EtherScan - API Descriptions ... + * + * @author GoodforGod + * @since 30.10.2018 + */ +public interface LogsAPI { + + /** + * alternative to the native eth_getLogs Read at EtherScan API description for full info! + * + * @param query build log query + * @return logs according to query + * @throws EtherScanException parent exception class + * @see LogQuery + */ + @NotNull + List logs(LogQuery query) throws EtherScanException; +} diff --git a/src/main/java/io/goodforgod/api/etherscan/LogsAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/LogsAPIProvider.java new file mode 100644 index 0000000..771d931 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/LogsAPIProvider.java @@ -0,0 +1,42 @@ +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.executor.EthHttpClient; +import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import io.goodforgod.api.etherscan.model.Log; +import io.goodforgod.api.etherscan.model.query.LogQuery; +import io.goodforgod.api.etherscan.model.response.LogResponseTO; +import io.goodforgod.api.etherscan.util.BasicUtils; +import java.util.Collections; +import java.util.List; +import org.jetbrains.annotations.NotNull; + +/** + * Logs API Implementation + * + * @see LogsAPI + * @author GoodforGod + * @since 28.10.2018 + */ +final class LogsAPIProvider extends BasicProvider implements LogsAPI { + + private static final String ACT_LOGS_PARAM = ACT_PREFIX + "getLogs"; + + LogsAPIProvider(RequestQueueManager queue, + String baseUrl, + EthHttpClient executor) { + super(queue, "logs", baseUrl, executor); + } + + @NotNull + @Override + public List logs(LogQuery query) throws EtherScanException { + final String urlParams = ACT_LOGS_PARAM + query.params(); + final LogResponseTO response = getRequest(urlParams, LogResponseTO.class); + BasicUtils.validateTxResponse(response); + + return (BasicUtils.isEmpty(response.getResult())) + ? Collections.emptyList() + : response.getResult(); + } +} diff --git a/src/main/java/io/api/etherscan/core/IProxyApi.java b/src/main/java/io/goodforgod/api/etherscan/ProxyAPI.java similarity index 63% rename from src/main/java/io/api/etherscan/core/IProxyApi.java rename to src/main/java/io/goodforgod/api/etherscan/ProxyAPI.java index b7e9f54..0785d13 100644 --- a/src/main/java/io/api/etherscan/core/IProxyApi.java +++ b/src/main/java/io/goodforgod/api/etherscan/ProxyAPI.java @@ -1,27 +1,27 @@ -package io.api.etherscan.core; +package io.goodforgod.api.etherscan; -import io.api.etherscan.error.ApiException; -import io.api.etherscan.model.proxy.BlockProxy; -import io.api.etherscan.model.proxy.ReceiptProxy; -import io.api.etherscan.model.proxy.TxProxy; +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.model.proxy.BlockProxy; +import io.goodforgod.api.etherscan.model.proxy.ReceiptProxy; +import io.goodforgod.api.etherscan.model.proxy.TxProxy; import java.math.BigInteger; import java.util.Optional; import org.jetbrains.annotations.ApiStatus.Experimental; import org.jetbrains.annotations.NotNull; /** - * EtherScan - API Descriptions https://etherscan.io/apis#proxy + * EtherScan - API Descriptions ... * * @author GoodforGod * @since 30.10.2018 */ -public interface IProxyApi { +public interface ProxyAPI { /** * Returns the number of most recent block eth_blockNumber * * @return last block number - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ long blockNoLast(); @@ -30,10 +30,10 @@ public interface IProxyApi { * * @param blockNo block number from 0 to last * @return optional block result - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - Optional block(long blockNo) throws ApiException; + Optional block(long blockNo) throws EtherScanException; /** * Returns information about a uncle by block number eth_getUncleByBlockNumberAndIndex @@ -41,10 +41,10 @@ public interface IProxyApi { * @param blockNo block number from 0 to last * @param index uncle block index * @return optional block result - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - Optional blockUncle(long blockNo, long index) throws ApiException; + Optional blockUncle(long blockNo, long index) throws EtherScanException; /** * Returns the information about a transaction requested by transaction hash @@ -52,10 +52,10 @@ public interface IProxyApi { * * @param txhash transaction hash * @return optional tx result - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - Optional tx(String txhash) throws ApiException; + Optional tx(String txhash) throws EtherScanException; /** * Returns information about a transaction by block number and transaction index position @@ -64,10 +64,10 @@ public interface IProxyApi { * @param blockNo block number from 0 to last * @param index tx index in block * @return optional tx result - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - Optional tx(long blockNo, long index) throws ApiException; + Optional tx(long blockNo, long index) throws EtherScanException; /** * Returns the number of transactions in a block from a block matching the given block number @@ -75,18 +75,18 @@ public interface IProxyApi { * * @param blockNo block number from 0 to last * @return transaction amount in block - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ - int txCount(long blockNo) throws ApiException; + int txCount(long blockNo) throws EtherScanException; /** * Returns the number of transactions sent from an address eth_getTransactionCount * * @param address eth address * @return transactions send amount from address - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ - int txSendCount(String address) throws ApiException; + int txSendCount(String address) throws EtherScanException; /** * Creates new message call transaction or a contract creation for signed transactions @@ -94,20 +94,20 @@ public interface IProxyApi { * * @param hexEncodedTx encoded hex data to send * @return optional string response - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - Optional txSendRaw(String hexEncodedTx) throws ApiException; + Optional txSendRaw(String hexEncodedTx) throws EtherScanException; /** * Returns the receipt of a transaction by transaction hash eth_getTransactionReceipt * * @param txhash transaction hash * @return optional tx receipt - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - Optional txReceipt(String txhash) throws ApiException; + Optional txReceipt(String txhash) throws EtherScanException; /** * Executes a new message call immediately without creating a transaction on the block chain @@ -116,20 +116,20 @@ public interface IProxyApi { * @param address to call * @param data data to call address * @return optional the return value of executed contract. - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - Optional call(String address, String data) throws ApiException; + Optional call(String address, String data) throws EtherScanException; /** * Returns code at a given address eth_getCode * * @param address get code from * @return optional the code from the given address - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - Optional code(String address) throws ApiException; + Optional code(String address) throws EtherScanException; /** * (**experimental) Returns the value from a storage position at a given address eth_getStorageAt @@ -137,20 +137,20 @@ public interface IProxyApi { * @param address to get storage * @param position storage position * @return optional the value at this storage position - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @Experimental @NotNull - Optional storageAt(String address, long position) throws ApiException; + Optional storageAt(String address, long position) throws EtherScanException; /** * Returns the current price per gas in wei eth_gasPrice * * @return estimated gas price - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - BigInteger gasPrice() throws ApiException; + BigInteger gasPrice() throws EtherScanException; /** * Makes a call or transaction, which won't be added to the blockchain and returns the used gas, @@ -158,11 +158,11 @@ public interface IProxyApi { * * @param hexData data to calc gas usage for * @return estimated gas usage - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - BigInteger gasEstimated(String hexData) throws ApiException; + BigInteger gasEstimated(String hexData) throws EtherScanException; @NotNull - BigInteger gasEstimated() throws ApiException; + BigInteger gasEstimated() throws EtherScanException; } diff --git a/src/main/java/io/api/etherscan/core/impl/ProxyApiProvider.java b/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java similarity index 75% rename from src/main/java/io/api/etherscan/core/impl/ProxyApiProvider.java rename to src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java index f456186..1239294 100644 --- a/src/main/java/io/api/etherscan/core/impl/ProxyApiProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java @@ -1,19 +1,18 @@ -package io.api.etherscan.core.impl; - -import io.api.etherscan.core.IProxyApi; -import io.api.etherscan.error.ApiException; -import io.api.etherscan.error.EtherScanException; -import io.api.etherscan.error.InvalidDataHexException; -import io.api.etherscan.executor.IHttpExecutor; -import io.api.etherscan.manager.IQueueManager; -import io.api.etherscan.model.proxy.BlockProxy; -import io.api.etherscan.model.proxy.ReceiptProxy; -import io.api.etherscan.model.proxy.TxProxy; -import io.api.etherscan.model.proxy.utility.BlockProxyTO; -import io.api.etherscan.model.proxy.utility.StringProxyTO; -import io.api.etherscan.model.proxy.utility.TxInfoProxyTO; -import io.api.etherscan.model.proxy.utility.TxProxyTO; -import io.api.etherscan.util.BasicUtils; +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.error.EtherScanInvalidDataHexException; +import io.goodforgod.api.etherscan.error.EtherScanResponseException; +import io.goodforgod.api.etherscan.executor.EthHttpClient; +import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import io.goodforgod.api.etherscan.model.proxy.BlockProxy; +import io.goodforgod.api.etherscan.model.proxy.ReceiptProxy; +import io.goodforgod.api.etherscan.model.proxy.TxProxy; +import io.goodforgod.api.etherscan.model.proxy.utility.BlockProxyTO; +import io.goodforgod.api.etherscan.model.proxy.utility.StringProxyTO; +import io.goodforgod.api.etherscan.model.proxy.utility.TxInfoProxyTO; +import io.goodforgod.api.etherscan.model.proxy.utility.TxProxyTO; +import io.goodforgod.api.etherscan.util.BasicUtils; import java.math.BigInteger; import java.util.Optional; import java.util.regex.Pattern; @@ -22,11 +21,11 @@ /** * Proxy API Implementation * - * @see IProxyApi + * @see ProxyAPI * @author GoodforGod * @since 28.10.2018 */ -public class ProxyApiProvider extends BasicProvider implements IProxyApi { +final class ProxyAPIProvider extends BasicProvider implements ProxyAPI { private static final String ACT_BLOCKNO_PARAM = ACT_PREFIX + "eth_blockNumber"; private static final String ACT_BY_BLOCKNO_PARAM = ACT_PREFIX + "eth_getBlockByNumber"; @@ -57,14 +56,14 @@ public class ProxyApiProvider extends BasicProvider implements IProxyApi { private static final Pattern EMPTY_HEX = Pattern.compile("0x0+"); - ProxyApiProvider(final IQueueManager queue, + ProxyAPIProvider(final RequestQueueManager queue, final String baseUrl, - final IHttpExecutor executor) { + final EthHttpClient executor) { super(queue, "proxy", baseUrl, executor); } @Override - public long blockNoLast() throws ApiException { + public long blockNoLast() throws EtherScanException { final StringProxyTO response = getRequest(ACT_BLOCKNO_PARAM, StringProxyTO.class); return (BasicUtils.isEmpty(response.getResult())) ? -1 @@ -73,7 +72,7 @@ public long blockNoLast() throws ApiException { @NotNull @Override - public Optional block(final long blockNo) throws ApiException { + public Optional block(long blockNo) throws EtherScanException { final long compBlockNo = BasicUtils.compensateMinBlock(blockNo); final String urlParams = ACT_BY_BLOCKNO_PARAM + TAG_PARAM + compBlockNo + BOOLEAN_PARAM; @@ -83,7 +82,7 @@ public Optional block(final long blockNo) throws ApiException { @NotNull @Override - public Optional blockUncle(final long blockNo, final long index) throws ApiException { + public Optional blockUncle(long blockNo, long index) throws EtherScanException { final long compBlockNo = BasicUtils.compensateMinBlock(blockNo); final long compIndex = BasicUtils.compensateMinBlock(index); @@ -95,7 +94,7 @@ public Optional blockUncle(final long blockNo, final long index) thr @NotNull @Override - public Optional tx(final String txhash) throws ApiException { + public Optional tx(String txhash) throws EtherScanException { BasicUtils.validateTxHash(txhash); final String urlParams = ACT_TX_BY_HASH_PARAM + TXHASH_PARAM + txhash; @@ -105,7 +104,7 @@ public Optional tx(final String txhash) throws ApiException { @NotNull @Override - public Optional tx(final long blockNo, final long index) throws ApiException { + public Optional tx(long blockNo, long index) throws EtherScanException { final long compBlockNo = BasicUtils.compensateMinBlock(blockNo); final long compIndex = (index < 1) ? 1 @@ -118,7 +117,7 @@ public Optional tx(final long blockNo, final long index) throws ApiExce } @Override - public int txCount(final long blockNo) throws ApiException { + public int txCount(long blockNo) throws EtherScanException { final long compensatedBlockNo = BasicUtils.compensateMinBlock(blockNo); final String urlParams = ACT_BLOCKTX_COUNT_PARAM + TAG_PARAM + "0x" + Long.toHexString(compensatedBlockNo); final StringProxyTO response = getRequest(urlParams, StringProxyTO.class); @@ -126,7 +125,7 @@ public int txCount(final long blockNo) throws ApiException { } @Override - public int txSendCount(final String address) throws ApiException { + public int txSendCount(String address) throws EtherScanException { BasicUtils.validateAddress(address); final String urlParams = ACT_TX_COUNT_PARAM + ADDRESS_PARAM + address + TAG_LAST_PARAM; @@ -136,14 +135,14 @@ public int txSendCount(final String address) throws ApiException { @Override @NotNull - public Optional txSendRaw(final String hexEncodedTx) throws ApiException { + public Optional txSendRaw(String hexEncodedTx) throws EtherScanException { if (BasicUtils.isNotHex(hexEncodedTx)) - throw new InvalidDataHexException("Data is not encoded in hex format - " + hexEncodedTx); + throw new EtherScanInvalidDataHexException("Data is not encoded in hex format - " + hexEncodedTx); final String urlParams = ACT_SEND_RAW_TX_PARAM + HEX_PARAM + hexEncodedTx; final StringProxyTO response = postRequest(urlParams, "", StringProxyTO.class); if (response.getError() != null) - throw new EtherScanException("Error occurred with code " + response.getError().getCode() + throw new EtherScanResponseException("Error occurred with code " + response.getError().getCode() + " with message " + response.getError().getMessage() + ", error id " + response.getId() + ", jsonRPC " + response.getJsonrpc()); @@ -152,7 +151,7 @@ public Optional txSendRaw(final String hexEncodedTx) throws ApiException @NotNull @Override - public Optional txReceipt(final String txhash) throws ApiException { + public Optional txReceipt(String txhash) throws EtherScanException { BasicUtils.validateTxHash(txhash); final String urlParams = ACT_TX_RECEIPT_PARAM + TXHASH_PARAM + txhash; @@ -162,10 +161,10 @@ public Optional txReceipt(final String txhash) throws ApiException @NotNull @Override - public Optional call(final String address, final String data) throws ApiException { + public Optional call(String address, String data) throws EtherScanException { BasicUtils.validateAddress(address); if (BasicUtils.isNotHex(data)) - throw new InvalidDataHexException("Data is not hex encoded."); + throw new EtherScanInvalidDataHexException("Data is not hex encoded."); final String urlParams = ACT_CALL_PARAM + TO_PARAM + address + DATA_PARAM + data + TAG_LAST_PARAM; final StringProxyTO response = getRequest(urlParams, StringProxyTO.class); @@ -174,7 +173,7 @@ public Optional call(final String address, final String data) throws Api @NotNull @Override - public Optional code(final String address) throws ApiException { + public Optional code(String address) throws EtherScanException { BasicUtils.validateAddress(address); final String urlParams = ACT_CODE_PARAM + ADDRESS_PARAM + address + TAG_LAST_PARAM; @@ -184,7 +183,7 @@ public Optional code(final String address) throws ApiException { @NotNull @Override - public Optional storageAt(final String address, final long position) throws ApiException { + public Optional storageAt(String address, long position) throws EtherScanException { BasicUtils.validateAddress(address); final long compPosition = BasicUtils.compensateMinBlock(position); @@ -197,7 +196,7 @@ public Optional storageAt(final String address, final long position) thr @NotNull @Override - public BigInteger gasPrice() throws ApiException { + public BigInteger gasPrice() throws EtherScanException { final StringProxyTO response = getRequest(ACT_GASPRICE_PARAM, StringProxyTO.class); return (BasicUtils.isEmpty(response.getResult())) ? BigInteger.valueOf(-1) @@ -206,15 +205,15 @@ public BigInteger gasPrice() throws ApiException { @NotNull @Override - public BigInteger gasEstimated() throws ApiException { + public BigInteger gasEstimated() throws EtherScanException { return gasEstimated("606060405260728060106000396000f360606040526000"); } @NotNull @Override - public BigInteger gasEstimated(final String hexData) throws ApiException { + public BigInteger gasEstimated(String hexData) throws EtherScanException { if (!BasicUtils.isEmpty(hexData) && BasicUtils.isNotHex(hexData)) - throw new InvalidDataHexException("Data is not in hex format."); + throw new EtherScanInvalidDataHexException("Data is not in hex format."); final String urlParams = ACT_ESTIMATEGAS_PARAM + DATA_PARAM + hexData + GAS_PARAM + "2000000000000000"; final StringProxyTO response = getRequest(urlParams, StringProxyTO.class); diff --git a/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java b/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java new file mode 100644 index 0000000..314f73e --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java @@ -0,0 +1,44 @@ +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.model.Price; +import io.goodforgod.api.etherscan.model.Supply; +import java.math.BigInteger; +import org.jetbrains.annotations.NotNull; + +/** + * EtherScan - API Descriptions ... + * + * @author GoodforGod + * @since 30.10.2018 + */ +public interface StatisticAPI { + + /** + * ERC20 token total Supply + * + * @param contract contract address + * @return token supply for specified contract + * @throws EtherScanException parent exception class + */ + @NotNull + BigInteger supply(String contract) throws EtherScanException; + + /** + * Eth total supply + * + * @return total ETH supply for moment + * @throws EtherScanException parent exception class + */ + @NotNull + Supply supply() throws EtherScanException; + + /** + * Eth last USD and BTC price + * + * @return last usd/btc price for ETH + * @throws EtherScanException parent exception class + */ + @NotNull + Price lastPrice() throws EtherScanException; +} diff --git a/src/main/java/io/api/etherscan/core/impl/StatisticApiProvider.java b/src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java similarity index 54% rename from src/main/java/io/api/etherscan/core/impl/StatisticApiProvider.java rename to src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java index a14119a..ee4bdaa 100644 --- a/src/main/java/io/api/etherscan/core/impl/StatisticApiProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java @@ -1,26 +1,25 @@ -package io.api.etherscan.core.impl; +package io.goodforgod.api.etherscan; -import io.api.etherscan.core.IStatisticApi; -import io.api.etherscan.error.ApiException; -import io.api.etherscan.error.EtherScanException; -import io.api.etherscan.executor.IHttpExecutor; -import io.api.etherscan.manager.IQueueManager; -import io.api.etherscan.model.Price; -import io.api.etherscan.model.Supply; -import io.api.etherscan.model.utility.PriceResponseTO; -import io.api.etherscan.model.utility.StringResponseTO; -import io.api.etherscan.util.BasicUtils; +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.error.EtherScanResponseException; +import io.goodforgod.api.etherscan.executor.EthHttpClient; +import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import io.goodforgod.api.etherscan.model.Price; +import io.goodforgod.api.etherscan.model.Supply; +import io.goodforgod.api.etherscan.model.response.PriceResponseTO; +import io.goodforgod.api.etherscan.model.response.StringResponseTO; +import io.goodforgod.api.etherscan.util.BasicUtils; import java.math.BigInteger; import org.jetbrains.annotations.NotNull; /** * Statistic API Implementation * - * @see IStatisticApi + * @see StatisticAPI * @author GoodforGod * @since 28.10.2018 */ -public class StatisticApiProvider extends BasicProvider implements IStatisticApi { +final class StatisticAPIProvider extends BasicProvider implements StatisticAPI { private static final String ACT_SUPPLY_PARAM = ACT_PREFIX + "ethsupply"; private static final String ACT_TOKEN_SUPPLY_PARAM = ACT_PREFIX + "tokensupply"; @@ -28,41 +27,41 @@ public class StatisticApiProvider extends BasicProvider implements IStatisticApi private static final String CONTRACT_ADDRESS_PARAM = "&contractaddress="; - StatisticApiProvider(final IQueueManager queue, + StatisticAPIProvider(final RequestQueueManager queue, final String baseUrl, - final IHttpExecutor executor) { + final EthHttpClient executor) { super(queue, "stats", baseUrl, executor); } @NotNull @Override - public Supply supply() throws ApiException { + public Supply supply() throws EtherScanException { final StringResponseTO response = getRequest(ACT_SUPPLY_PARAM, StringResponseTO.class); if (response.getStatus() != 1) - throw new EtherScanException(response); + throw new EtherScanResponseException(response); return new Supply(new BigInteger(response.getResult())); } @NotNull @Override - public BigInteger supply(final String contract) throws ApiException { + public BigInteger supply(String contract) throws EtherScanException { BasicUtils.validateAddress(contract); final String urlParams = ACT_TOKEN_SUPPLY_PARAM + CONTRACT_ADDRESS_PARAM + contract; final StringResponseTO response = getRequest(urlParams, StringResponseTO.class); if (response.getStatus() != 1) - throw new EtherScanException(response); + throw new EtherScanResponseException(response); return new BigInteger(response.getResult()); } @NotNull @Override - public Price lastPrice() throws ApiException { + public Price lastPrice() throws EtherScanException { final PriceResponseTO response = getRequest(ACT_LASTPRICE_PARAM, PriceResponseTO.class); if (response.getStatus() != 1) - throw new EtherScanException(response); + throw new EtherScanResponseException(response); return response.getResult(); } diff --git a/src/main/java/io/api/etherscan/core/ITransactionApi.java b/src/main/java/io/goodforgod/api/etherscan/TransactionAPI.java similarity index 51% rename from src/main/java/io/api/etherscan/core/ITransactionApi.java rename to src/main/java/io/goodforgod/api/etherscan/TransactionAPI.java index 4180ff4..6bfc545 100644 --- a/src/main/java/io/api/etherscan/core/ITransactionApi.java +++ b/src/main/java/io/goodforgod/api/etherscan/TransactionAPI.java @@ -1,35 +1,35 @@ -package io.api.etherscan.core; +package io.goodforgod.api.etherscan; -import io.api.etherscan.error.ApiException; -import io.api.etherscan.model.Status; +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.model.Status; import java.util.Optional; import org.jetbrains.annotations.NotNull; /** - * EtherScan - API Descriptions https://etherscan.io/apis#transactions + * EtherScan - API Descriptions ... * * @author GoodforGod * @since 30.10.2018 */ -public interface ITransactionApi { +public interface TransactionAPI { /** * Check Contract Execution Status (if there was an error during contract execution) * * @param txhash transaction hash * @return optional status result - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - Optional execStatus(String txhash) throws ApiException; + Optional statusExec(String txhash) throws EtherScanException; /** * Check Transaction Receipt Status (Only applicable for Post Byzantium fork transactions) * * @param txhash transaction hash * @return 0 = Fail, 1 = Pass, empty value for pre-byzantium fork - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - Optional receiptStatus(String txhash) throws ApiException; + Optional statusReceipt(String txhash) throws EtherScanException; } diff --git a/src/main/java/io/api/etherscan/core/impl/TransactionApiProvider.java b/src/main/java/io/goodforgod/api/etherscan/TransactionAPIProvider.java similarity index 60% rename from src/main/java/io/api/etherscan/core/impl/TransactionApiProvider.java rename to src/main/java/io/goodforgod/api/etherscan/TransactionAPIProvider.java index 1c83bf0..91082a8 100644 --- a/src/main/java/io/api/etherscan/core/impl/TransactionApiProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/TransactionAPIProvider.java @@ -1,13 +1,12 @@ -package io.api.etherscan.core.impl; - -import io.api.etherscan.core.ITransactionApi; -import io.api.etherscan.error.ApiException; -import io.api.etherscan.executor.IHttpExecutor; -import io.api.etherscan.manager.IQueueManager; -import io.api.etherscan.model.Status; -import io.api.etherscan.model.utility.ReceiptStatusResponseTO; -import io.api.etherscan.model.utility.StatusResponseTO; -import io.api.etherscan.util.BasicUtils; +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.executor.EthHttpClient; +import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import io.goodforgod.api.etherscan.model.Status; +import io.goodforgod.api.etherscan.model.response.ReceiptStatusResponseTO; +import io.goodforgod.api.etherscan.model.response.StatusResponseTO; +import io.goodforgod.api.etherscan.util.BasicUtils; import java.util.Optional; import org.jetbrains.annotations.NotNull; @@ -15,25 +14,25 @@ * Transaction API Implementation * * @author GoodforGod - * @see ITransactionApi + * @see TransactionAPI * @since 28.10.2018 */ -public class TransactionApiProvider extends BasicProvider implements ITransactionApi { +final class TransactionAPIProvider extends BasicProvider implements TransactionAPI { private static final String ACT_EXEC_STATUS_PARAM = ACT_PREFIX + "getstatus"; private static final String ACT_RECEIPT_STATUS_PARAM = ACT_PREFIX + "gettxreceiptstatus"; private static final String TXHASH_PARAM = "&txhash="; - TransactionApiProvider(final IQueueManager queue, - final String baseUrl, - final IHttpExecutor executor) { + TransactionAPIProvider(RequestQueueManager queue, + String baseUrl, + EthHttpClient executor) { super(queue, "transaction", baseUrl, executor); } @NotNull @Override - public Optional execStatus(final String txhash) throws ApiException { + public Optional statusExec(String txhash) throws EtherScanException { BasicUtils.validateTxHash(txhash); final String urlParams = ACT_EXEC_STATUS_PARAM + TXHASH_PARAM + txhash; @@ -45,7 +44,7 @@ public Optional execStatus(final String txhash) throws ApiException { @NotNull @Override - public Optional receiptStatus(final String txhash) throws ApiException { + public Optional statusReceipt(String txhash) throws EtherScanException { BasicUtils.validateTxHash(txhash); final String urlParams = ACT_RECEIPT_STATUS_PARAM + TXHASH_PARAM + txhash; diff --git a/src/main/java/io/goodforgod/api/etherscan/error/ErtherScanLogQueryException.java b/src/main/java/io/goodforgod/api/etherscan/error/ErtherScanLogQueryException.java new file mode 100644 index 0000000..b39dcee --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/error/ErtherScanLogQueryException.java @@ -0,0 +1,12 @@ +package io.goodforgod.api.etherscan.error; + +/** + * @author GoodforGod + * @since 31.10.2018 + */ +public class ErtherScanLogQueryException extends EtherScanException { + + public ErtherScanLogQueryException(String message) { + super(message); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanConnectionException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanConnectionException.java new file mode 100644 index 0000000..731592c --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanConnectionException.java @@ -0,0 +1,16 @@ +package io.goodforgod.api.etherscan.error; + +/** + * @author GoodforGod + * @since 29.10.2018 + */ +public class EtherScanConnectionException extends EtherScanException { + + public EtherScanConnectionException(String message) { + super(message); + } + + public EtherScanConnectionException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanException.java new file mode 100644 index 0000000..67cc3bb --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanException.java @@ -0,0 +1,16 @@ +package io.goodforgod.api.etherscan.error; + +/** + * @author GoodforGod + * @since 30.10.2018 + */ +public class EtherScanException extends RuntimeException { + + public EtherScanException(String message) { + super(message); + } + + public EtherScanException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidAddressException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidAddressException.java new file mode 100644 index 0000000..ff12e3c --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidAddressException.java @@ -0,0 +1,12 @@ +package io.goodforgod.api.etherscan.error; + +/** + * @author GoodforGod + * @since 29.10.2018 + */ +public class EtherScanInvalidAddressException extends EtherScanException { + + public EtherScanInvalidAddressException(String message) { + super(message); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidDataHexException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidDataHexException.java new file mode 100644 index 0000000..b5b5f2d --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidDataHexException.java @@ -0,0 +1,12 @@ +package io.goodforgod.api.etherscan.error; + +/** + * @author GoodforGod + * @since 02.11.2018 + */ +public class EtherScanInvalidDataHexException extends EtherScanException { + + public EtherScanInvalidDataHexException(String message) { + super(message); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidTxHashException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidTxHashException.java new file mode 100644 index 0000000..33be4ad --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanInvalidTxHashException.java @@ -0,0 +1,12 @@ +package io.goodforgod.api.etherscan.error; + +/** + * @author GoodforGod + * @since 02.11.2018 + */ +public class EtherScanInvalidTxHashException extends EtherScanException { + + public EtherScanInvalidTxHashException(String message) { + super(message); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanKeyException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanKeyException.java new file mode 100644 index 0000000..f9e4aa8 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanKeyException.java @@ -0,0 +1,12 @@ +package io.goodforgod.api.etherscan.error; + +/** + * @author GoodforGod + * @since 05.11.2018 + */ +public class EtherScanKeyException extends EtherScanException { + + public EtherScanKeyException(String message) { + super(message); + } +} diff --git a/src/main/java/io/api/etherscan/error/ParseException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanParseException.java similarity index 52% rename from src/main/java/io/api/etherscan/error/ParseException.java rename to src/main/java/io/goodforgod/api/etherscan/error/EtherScanParseException.java index 5dc6199..87116ab 100644 --- a/src/main/java/io/api/etherscan/error/ParseException.java +++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanParseException.java @@ -1,14 +1,14 @@ -package io.api.etherscan.error; +package io.goodforgod.api.etherscan.error; /** * @author GoodforGod * @since 29.10.2018 */ -public class ParseException extends ApiException { +public class EtherScanParseException extends EtherScanException { private final String json; - public ParseException(String message, Throwable cause, String json) { + public EtherScanParseException(String message, Throwable cause, String json) { super(message, cause); this.json = json; } diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanRateLimitException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanRateLimitException.java new file mode 100644 index 0000000..eefb1ea --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanRateLimitException.java @@ -0,0 +1,12 @@ +package io.goodforgod.api.etherscan.error; + +/** + * @author iSnow + * @since 2020-10-06 + */ +public class EtherScanRateLimitException extends EtherScanException { + + public EtherScanRateLimitException(String message) { + super(message); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanResponseException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanResponseException.java new file mode 100644 index 0000000..21da798 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanResponseException.java @@ -0,0 +1,23 @@ +package io.goodforgod.api.etherscan.error; + +import io.goodforgod.api.etherscan.model.response.BaseResponseTO; +import io.goodforgod.api.etherscan.model.response.StringResponseTO; + +/** + * @author GoodforGod + * @since 29.10.2018 + */ +public class EtherScanResponseException extends EtherScanException { + + public EtherScanResponseException(BaseResponseTO response) { + this(response.getMessage() + ", with status: " + response.getStatus()); + } + + public EtherScanResponseException(StringResponseTO response) { + this(response.getResult() + ", with status: " + response.getStatus() + ", with message: " + response.getMessage()); + } + + public EtherScanResponseException(String message) { + super(message); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanTimeoutException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanTimeoutException.java new file mode 100644 index 0000000..7734139 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanTimeoutException.java @@ -0,0 +1,12 @@ +package io.goodforgod.api.etherscan.error; + +/** + * @author GoodforGod + * @since 12.11.2018 + */ +public class EtherScanTimeoutException extends EtherScanConnectionException { + + public EtherScanTimeoutException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/io/api/etherscan/executor/IHttpExecutor.java b/src/main/java/io/goodforgod/api/etherscan/executor/EthHttpClient.java similarity index 84% rename from src/main/java/io/api/etherscan/executor/IHttpExecutor.java rename to src/main/java/io/goodforgod/api/etherscan/executor/EthHttpClient.java index 0c80282..4edc507 100644 --- a/src/main/java/io/api/etherscan/executor/IHttpExecutor.java +++ b/src/main/java/io/goodforgod/api/etherscan/executor/EthHttpClient.java @@ -1,4 +1,4 @@ -package io.api.etherscan.executor; +package io.goodforgod.api.etherscan.executor; /** * Http Client interface @@ -6,7 +6,7 @@ * @author GoodforGod * @since 31.10.2018 */ -public interface IHttpExecutor { +public interface EthHttpClient { /** * Performs a Http GET request diff --git a/src/main/java/io/api/etherscan/executor/impl/HttpExecutor.java b/src/main/java/io/goodforgod/api/etherscan/executor/impl/UrlEthHttpClient.java similarity index 66% rename from src/main/java/io/api/etherscan/executor/impl/HttpExecutor.java rename to src/main/java/io/goodforgod/api/etherscan/executor/impl/UrlEthHttpClient.java index 49e7fee..ac05125 100644 --- a/src/main/java/io/api/etherscan/executor/impl/HttpExecutor.java +++ b/src/main/java/io/goodforgod/api/etherscan/executor/impl/UrlEthHttpClient.java @@ -1,11 +1,11 @@ -package io.api.etherscan.executor.impl; +package io.goodforgod.api.etherscan.executor.impl; import static java.net.HttpURLConnection.*; -import io.api.etherscan.error.ApiTimeoutException; -import io.api.etherscan.error.ConnectionException; -import io.api.etherscan.executor.IHttpExecutor; -import io.api.etherscan.util.BasicUtils; +import io.goodforgod.api.etherscan.error.EtherScanConnectionException; +import io.goodforgod.api.etherscan.error.EtherScanTimeoutException; +import io.goodforgod.api.etherscan.executor.EthHttpClient; +import io.goodforgod.api.etherscan.util.BasicUtils; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -14,6 +14,8 @@ import java.net.SocketTimeoutException; import java.net.URL; import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.zip.GZIPInputStream; @@ -23,15 +25,15 @@ * Http client implementation * * @author GoodforGod - * @see IHttpExecutor + * @see EthHttpClient * @since 28.10.2018 */ -public class HttpExecutor implements IHttpExecutor { +public final class UrlEthHttpClient implements EthHttpClient { private static final Map DEFAULT_HEADERS = new HashMap<>(); - private static final int CONNECT_TIMEOUT = 8000; - private static final int READ_TIMEOUT = 0; + private static final Duration DEFAULT_CONNECT_TIMEOUT = Duration.ofSeconds(8); + private static final Duration DEFAULT_READ_TIMEOUT = Duration.ZERO; static { DEFAULT_HEADERS.put("Accept-Language", "en"); @@ -44,15 +46,15 @@ public class HttpExecutor implements IHttpExecutor { private final int connectTimeout; private final int readTimeout; - public HttpExecutor() { - this(CONNECT_TIMEOUT); + public UrlEthHttpClient() { + this(DEFAULT_CONNECT_TIMEOUT); } - public HttpExecutor(final int connectTimeout) { - this(connectTimeout, READ_TIMEOUT); + public UrlEthHttpClient(Duration connectTimeout) { + this(connectTimeout, DEFAULT_READ_TIMEOUT); } - public HttpExecutor(final int connectTimeout, final int readTimeout) { + public UrlEthHttpClient(Duration connectTimeout, Duration readTimeout) { this(connectTimeout, readTimeout, DEFAULT_HEADERS); } @@ -61,12 +63,12 @@ public HttpExecutor(final int connectTimeout, final int readTimeout) { * @param readTimeout custom read timeout in millis * @param headers custom HTTP headers */ - public HttpExecutor(final int connectTimeout, - final int readTimeout, - final Map headers) { - this.connectTimeout = Math.max(connectTimeout, 0); - this.readTimeout = Math.max(readTimeout, 0); - this.headers = headers; + public UrlEthHttpClient(Duration connectTimeout, + Duration readTimeout, + Map headers) { + this.connectTimeout = Math.toIntExact(connectTimeout.toMillis()); + this.readTimeout = Math.toIntExact(readTimeout.toMillis()); + this.headers = Collections.unmodifiableMap(headers); } private HttpURLConnection buildConnection(String urlAsString, String method) throws IOException { @@ -87,23 +89,23 @@ public String get(final String urlAsString) { if (status == HTTP_MOVED_TEMP || status == HTTP_MOVED_PERM) { return get(connection.getHeaderField("Location")); } else if ((status >= HTTP_BAD_REQUEST) && (status < HTTP_INTERNAL_ERROR)) { - throw new ConnectionException("Protocol error: " + connection.getResponseMessage()); + throw new EtherScanConnectionException("Protocol error: " + connection.getResponseMessage()); } else if (status >= HTTP_INTERNAL_ERROR) { - throw new ConnectionException("Server error: " + connection.getResponseMessage()); + throw new EtherScanConnectionException("Server error: " + connection.getResponseMessage()); } final String data = readData(connection); connection.disconnect(); return data; } catch (SocketTimeoutException e) { - throw new ApiTimeoutException("Timeout: Could not establish connection for " + connectTimeout + " millis", e); + throw new EtherScanTimeoutException("Timeout: Could not establish connection for " + connectTimeout + " millis", e); } catch (Exception e) { - throw new ConnectionException(e.getMessage(), e); + throw new EtherScanConnectionException(e.getMessage(), e); } } @Override - public String post(final String urlAsString, final String dataToPost) { + public String post(String urlAsString, String dataToPost) { try { final HttpURLConnection connection = buildConnection(urlAsString, "POST"); final String contentLength = (BasicUtils.isBlank(dataToPost)) @@ -123,22 +125,22 @@ public String post(final String urlAsString, final String dataToPost) { if (status == HTTP_MOVED_TEMP || status == HTTP_MOVED_PERM) { return post(connection.getHeaderField("Location"), dataToPost); } else if ((status >= HTTP_BAD_REQUEST) && (status < HTTP_INTERNAL_ERROR)) { - throw new ConnectionException("Protocol error: " + connection.getResponseMessage()); + throw new EtherScanConnectionException("Protocol error: " + connection.getResponseMessage()); } else if (status >= HTTP_INTERNAL_ERROR) { - throw new ConnectionException("Server error: " + connection.getResponseMessage()); + throw new EtherScanConnectionException("Server error: " + connection.getResponseMessage()); } final String data = readData(connection); connection.disconnect(); return data; } catch (SocketTimeoutException e) { - throw new ApiTimeoutException("Timeout: Could not establish connection for " + connectTimeout + " millis", e); + throw new EtherScanTimeoutException("Timeout: Could not establish connection for " + connectTimeout + " millis", e); } catch (Exception e) { - throw new ConnectionException(e.getMessage(), e); + throw new EtherScanConnectionException(e.getMessage(), e); } } - private String readData(final HttpURLConnection connection) throws IOException { + private String readData(HttpURLConnection connection) throws IOException { final StringBuilder content = new StringBuilder(); try (BufferedReader in = new BufferedReader(getStreamReader(connection))) { String inputLine; @@ -149,7 +151,7 @@ private String readData(final HttpURLConnection connection) throws IOException { return content.toString(); } - private InputStreamReader getStreamReader(final HttpURLConnection connection) throws IOException { + private InputStreamReader getStreamReader(HttpURLConnection connection) throws IOException { switch (String.valueOf(connection.getContentEncoding())) { case "gzip": return new InputStreamReader(new GZIPInputStream(connection.getInputStream()), StandardCharsets.UTF_8); diff --git a/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java b/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java new file mode 100644 index 0000000..7472c3f --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java @@ -0,0 +1,24 @@ +package io.goodforgod.api.etherscan.manager; + +import io.goodforgod.api.etherscan.manager.impl.SemaphoreRequestQueueManager; +import java.time.Duration; + +/** + * 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 RequestQueueManager extends AutoCloseable { + + RequestQueueManager DEFAULT_KEY_QUEUE = new SemaphoreRequestQueueManager(1, Duration.ofMillis(5050L), + Duration.ofMillis(5050L), 0); + RequestQueueManager PERSONAL_KEY_QUEUE = new SemaphoreRequestQueueManager(5, Duration.ofMillis(1050L), + Duration.ofMillis(1050L), 5); + + /** + * Waits in queue for chance to take turn + */ + void takeTurn(); +} diff --git a/src/main/java/io/api/etherscan/manager/impl/FakeQueueManager.java b/src/main/java/io/goodforgod/api/etherscan/manager/impl/FakeRequestQueueManager.java similarity index 65% rename from src/main/java/io/api/etherscan/manager/impl/FakeQueueManager.java rename to src/main/java/io/goodforgod/api/etherscan/manager/impl/FakeRequestQueueManager.java index 620244c..626b4c1 100644 --- a/src/main/java/io/api/etherscan/manager/impl/FakeQueueManager.java +++ b/src/main/java/io/goodforgod/api/etherscan/manager/impl/FakeRequestQueueManager.java @@ -1,6 +1,6 @@ -package io.api.etherscan.manager.impl; +package io.goodforgod.api.etherscan.manager.impl; -import io.api.etherscan.manager.IQueueManager; +import io.goodforgod.api.etherscan.manager.RequestQueueManager; /** * Fake queue manager, always give turns, when you have no limits @@ -8,7 +8,7 @@ * @author GoodforGod * @since 03.11.2018 */ -public class FakeQueueManager implements IQueueManager { +public final class FakeRequestQueueManager implements RequestQueueManager { @Override public void takeTurn() { diff --git a/src/main/java/io/goodforgod/api/etherscan/manager/impl/SemaphoreRequestQueueManager.java b/src/main/java/io/goodforgod/api/etherscan/manager/impl/SemaphoreRequestQueueManager.java new file mode 100644 index 0000000..cfd745f --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/manager/impl/SemaphoreRequestQueueManager.java @@ -0,0 +1,53 @@ +package io.goodforgod.api.etherscan.manager.impl; + +import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import java.time.Duration; +import java.util.concurrent.*; + +/** + * Queue Semaphore implementation with size and reset time as params + * + * @see RequestQueueManager + * @author GoodforGod + * @since 30.10.2018 + */ +public final class SemaphoreRequestQueueManager implements RequestQueueManager, AutoCloseable { + + private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor(); + private final Semaphore semaphore; + private final long queueResetTimeInMillis; + + public SemaphoreRequestQueueManager(int size, Duration resetIn) { + this(size, resetIn, resetIn); + } + + public SemaphoreRequestQueueManager(int size, Duration resetIn, Duration delayIn) { + this(size, resetIn, delayIn, size); + } + + public SemaphoreRequestQueueManager(int size, Duration queueResetTimeIn, Duration delayIn, int initialSize) { + this.semaphore = new Semaphore(initialSize); + this.queueResetTimeInMillis = queueResetTimeIn.toMillis(); + this.executorService.scheduleAtFixedRate(releaseLocks(size + 1), delayIn.toMillis(), queueResetTimeInMillis, + TimeUnit.MILLISECONDS); + } + + @SuppressWarnings("java:S899") + @Override + public void takeTurn() { + try { + semaphore.tryAcquire(queueResetTimeInMillis, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + private Runnable releaseLocks(int toRelease) { + return () -> semaphore.release(toRelease); + } + + @Override + public void close() { + executorService.shutdown(); + } +} diff --git a/src/main/java/io/api/etherscan/model/Abi.java b/src/main/java/io/goodforgod/api/etherscan/model/Abi.java similarity index 94% rename from src/main/java/io/api/etherscan/model/Abi.java rename to src/main/java/io/goodforgod/api/etherscan/model/Abi.java index 880e6a0..b575c56 100644 --- a/src/main/java/io/api/etherscan/model/Abi.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Abi.java @@ -1,6 +1,6 @@ -package io.api.etherscan.model; +package io.goodforgod.api.etherscan.model; -import io.api.etherscan.util.BasicUtils; +import io.goodforgod.api.etherscan.util.BasicUtils; /** * @author GoodforGod diff --git a/src/main/java/io/api/etherscan/model/Balance.java b/src/main/java/io/goodforgod/api/etherscan/model/Balance.java similarity index 94% rename from src/main/java/io/api/etherscan/model/Balance.java rename to src/main/java/io/goodforgod/api/etherscan/model/Balance.java index ed6d6c5..d147a2c 100644 --- a/src/main/java/io/api/etherscan/model/Balance.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Balance.java @@ -1,6 +1,6 @@ -package io.api.etherscan.model; +package io.goodforgod.api.etherscan.model; -import io.api.etherscan.model.utility.BalanceTO; +import io.goodforgod.api.etherscan.model.response.BalanceTO; import java.math.BigInteger; import java.util.Objects; diff --git a/src/main/java/io/api/etherscan/model/BaseTx.java b/src/main/java/io/goodforgod/api/etherscan/model/BaseTx.java similarity index 97% rename from src/main/java/io/api/etherscan/model/BaseTx.java rename to src/main/java/io/goodforgod/api/etherscan/model/BaseTx.java index 3942d14..1fb53e1 100644 --- a/src/main/java/io/api/etherscan/model/BaseTx.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/BaseTx.java @@ -1,7 +1,7 @@ -package io.api.etherscan.model; +package io.goodforgod.api.etherscan.model; import com.google.gson.annotations.Expose; -import io.api.etherscan.util.BasicUtils; +import io.goodforgod.api.etherscan.util.BasicUtils; import java.math.BigInteger; import java.time.LocalDateTime; import java.time.ZoneOffset; diff --git a/src/main/java/io/api/etherscan/model/Block.java b/src/main/java/io/goodforgod/api/etherscan/model/Block.java similarity index 94% rename from src/main/java/io/api/etherscan/model/Block.java rename to src/main/java/io/goodforgod/api/etherscan/model/Block.java index f5e8b6a..8b652bb 100644 --- a/src/main/java/io/api/etherscan/model/Block.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Block.java @@ -1,7 +1,7 @@ -package io.api.etherscan.model; +package io.goodforgod.api.etherscan.model; import com.google.gson.annotations.Expose; -import io.api.etherscan.util.BasicUtils; +import io.goodforgod.api.etherscan.util.BasicUtils; import java.math.BigInteger; import java.time.LocalDateTime; import java.time.ZoneOffset; diff --git a/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java b/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java new file mode 100644 index 0000000..34f8f30 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java @@ -0,0 +1,128 @@ +package io.goodforgod.api.etherscan.model; + +import io.goodforgod.api.etherscan.util.BasicUtils; + +import java.math.BigInteger; +import java.util.List; + +/** + * @author GoodforGod + * @since 30.10.2018 + */ +public class BlockUncle extends Block { + + public static class Uncle { + + private String miner; + private BigInteger blockreward; + private int unclePosition; + + // + public String getMiner() { + return miner; + } + + public BigInteger getBlockreward() { + return blockreward; + } + + public int getUnclePosition() { + return unclePosition; + } + // + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + + Uncle uncle = (Uncle) o; + if (unclePosition != uncle.unclePosition) + return false; + if (miner != null + ? !miner.equals(uncle.miner) + : uncle.miner != null) + return false; + return blockreward != null + ? blockreward.equals(uncle.blockreward) + : uncle.blockreward == null; + } + + @Override + public int hashCode() { + int result = miner != null + ? miner.hashCode() + : 0; + result = 31 * result + (blockreward != null + ? blockreward.hashCode() + : 0); + result = 31 * result + unclePosition; + return result; + } + + @Override + public String toString() { + return "Uncle{" + + "miner='" + miner + '\'' + + ", blockreward=" + blockreward + + ", unclePosition=" + unclePosition + + '}'; + } + } + + private String blockMiner; + private List uncles; + private String uncleInclusionReward; + + // + public boolean isEmpty() { + return getBlockNumber() == 0 && getBlockReward() == null + && getTimeStamp() == null + && BasicUtils.isEmpty(blockMiner); + } + + public String getBlockMiner() { + return blockMiner; + } + + public List getUncles() { + return uncles; + } + + public String getUncleInclusionReward() { + return uncleInclusionReward; + } + // + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + if (!super.equals(o)) + return false; + + BlockUncle that = (BlockUncle) o; + + return getBlockNumber() != 0 && getBlockNumber() == that.getBlockNumber(); + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = (int) (31 * result + getBlockNumber()); + return result; + } + + @Override + public String toString() { + return "UncleBlock{" + + "blockMiner='" + blockMiner + '\'' + + ", uncles=" + uncles + + ", uncleInclusionReward='" + uncleInclusionReward + '\'' + + '}'; + } +} diff --git a/src/main/java/io/api/etherscan/model/Log.java b/src/main/java/io/goodforgod/api/etherscan/model/Log.java similarity index 98% rename from src/main/java/io/api/etherscan/model/Log.java rename to src/main/java/io/goodforgod/api/etherscan/model/Log.java index 595122b..cc22b66 100644 --- a/src/main/java/io/api/etherscan/model/Log.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Log.java @@ -1,7 +1,7 @@ -package io.api.etherscan.model; +package io.goodforgod.api.etherscan.model; import com.google.gson.annotations.Expose; -import io.api.etherscan.util.BasicUtils; +import io.goodforgod.api.etherscan.util.BasicUtils; import java.math.BigInteger; import java.time.LocalDateTime; import java.time.ZoneOffset; diff --git a/src/main/java/io/api/etherscan/model/Price.java b/src/main/java/io/goodforgod/api/etherscan/model/Price.java similarity index 98% rename from src/main/java/io/api/etherscan/model/Price.java rename to src/main/java/io/goodforgod/api/etherscan/model/Price.java index fc72ab5..712dbd8 100644 --- a/src/main/java/io/api/etherscan/model/Price.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Price.java @@ -1,4 +1,4 @@ -package io.api.etherscan.model; +package io.goodforgod.api.etherscan.model; import com.google.gson.annotations.Expose; import java.time.LocalDateTime; diff --git a/src/main/java/io/api/etherscan/model/Status.java b/src/main/java/io/goodforgod/api/etherscan/model/Status.java similarity index 96% rename from src/main/java/io/api/etherscan/model/Status.java rename to src/main/java/io/goodforgod/api/etherscan/model/Status.java index 2017cd7..274080a 100644 --- a/src/main/java/io/api/etherscan/model/Status.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Status.java @@ -1,4 +1,4 @@ -package io.api.etherscan.model; +package io.goodforgod.api.etherscan.model; import java.util.Objects; diff --git a/src/main/java/io/api/etherscan/model/Supply.java b/src/main/java/io/goodforgod/api/etherscan/model/Supply.java similarity index 81% rename from src/main/java/io/api/etherscan/model/Supply.java rename to src/main/java/io/goodforgod/api/etherscan/model/Supply.java index f495aaf..80dc7d0 100644 --- a/src/main/java/io/api/etherscan/model/Supply.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Supply.java @@ -1,4 +1,4 @@ -package io.api.etherscan.model; +package io.goodforgod.api.etherscan.model; import java.math.BigInteger; diff --git a/src/main/java/io/api/etherscan/model/TokenBalance.java b/src/main/java/io/goodforgod/api/etherscan/model/TokenBalance.java similarity index 96% rename from src/main/java/io/api/etherscan/model/TokenBalance.java rename to src/main/java/io/goodforgod/api/etherscan/model/TokenBalance.java index 684738c..d42fd05 100644 --- a/src/main/java/io/api/etherscan/model/TokenBalance.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TokenBalance.java @@ -1,4 +1,4 @@ -package io.api.etherscan.model; +package io.goodforgod.api.etherscan.model; import java.math.BigInteger; import java.util.Objects; diff --git a/src/main/java/io/api/etherscan/model/Tx.java b/src/main/java/io/goodforgod/api/etherscan/model/Tx.java similarity index 96% rename from src/main/java/io/api/etherscan/model/Tx.java rename to src/main/java/io/goodforgod/api/etherscan/model/Tx.java index 13b5292..3ab0923 100644 --- a/src/main/java/io/api/etherscan/model/Tx.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Tx.java @@ -1,6 +1,6 @@ -package io.api.etherscan.model; +package io.goodforgod.api.etherscan.model; -import io.api.etherscan.util.BasicUtils; +import io.goodforgod.api.etherscan.util.BasicUtils; import java.math.BigInteger; import java.util.Objects; diff --git a/src/main/java/io/api/etherscan/model/TxToken.java b/src/main/java/io/goodforgod/api/etherscan/model/TxERC20.java similarity index 93% rename from src/main/java/io/api/etherscan/model/TxToken.java rename to src/main/java/io/goodforgod/api/etherscan/model/TxERC20.java index c455ffb..9f65d98 100644 --- a/src/main/java/io/api/etherscan/model/TxToken.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxERC20.java @@ -1,10 +1,10 @@ -package io.api.etherscan.model; +package io.goodforgod.api.etherscan.model; /** * @author GoodforGod * @since 28.10.2018 */ -public class TxToken extends BaseTx { +public class TxERC20 extends BaseTx { private long nonce; private String blockHash; @@ -56,7 +56,7 @@ public long getConfirmations() { @Override public String toString() { - return "TxToken{" + + return "TxERC20{" + "nonce=" + nonce + ", blockHash='" + blockHash + '\'' + ", tokenName='" + tokenName + '\'' + diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxERC721.java b/src/main/java/io/goodforgod/api/etherscan/model/TxERC721.java new file mode 100644 index 0000000..5b30314 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxERC721.java @@ -0,0 +1,71 @@ +package io.goodforgod.api.etherscan.model; + +/** + * @author GoodforGod + * @since 28.10.2018 + */ +public class TxERC721 extends BaseTx { + + private long nonce; + private String blockHash; + private String tokenName; + private String tokenSymbol; + private String tokenDecimal; + private int transactionIndex; + private long gasPrice; + private long cumulativeGasUsed; + private long confirmations; + + // + public long getNonce() { + return nonce; + } + + public String getBlockHash() { + return blockHash; + } + + public String getTokenName() { + return tokenName; + } + + public String getTokenSymbol() { + return tokenSymbol; + } + + public String getTokenDecimal() { + return tokenDecimal; + } + + public int getTransactionIndex() { + return transactionIndex; + } + + public long getGasPrice() { + return gasPrice; + } + + public long getCumulativeGasUsed() { + return cumulativeGasUsed; + } + + public long getConfirmations() { + return confirmations; + } + // + + @Override + public String toString() { + return "TxERC721{" + + "nonce=" + nonce + + ", blockHash='" + blockHash + '\'' + + ", tokenName='" + tokenName + '\'' + + ", tokenSymbol='" + tokenSymbol + '\'' + + ", tokenDecimal='" + tokenDecimal + '\'' + + ", transactionIndex=" + transactionIndex + + ", gasPrice=" + gasPrice + + ", cumulativeGasUsed=" + cumulativeGasUsed + + ", confirmations=" + confirmations + + "} " + super.toString(); + } +} diff --git a/src/main/java/io/api/etherscan/model/TxInternal.java b/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java similarity index 97% rename from src/main/java/io/api/etherscan/model/TxInternal.java rename to src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java index 5471268..244e0b7 100644 --- a/src/main/java/io/api/etherscan/model/TxInternal.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java @@ -1,4 +1,4 @@ -package io.api.etherscan.model; +package io.goodforgod.api.etherscan.model; import java.util.Objects; diff --git a/src/main/java/io/api/etherscan/model/Wei.java b/src/main/java/io/goodforgod/api/etherscan/model/Wei.java similarity index 96% rename from src/main/java/io/api/etherscan/model/Wei.java rename to src/main/java/io/goodforgod/api/etherscan/model/Wei.java index 0735d90..224a1cf 100644 --- a/src/main/java/io/api/etherscan/model/Wei.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Wei.java @@ -1,4 +1,4 @@ -package io.api.etherscan.model; +package io.goodforgod.api.etherscan.model; import java.math.BigInteger; import java.util.Objects; diff --git a/src/main/java/io/api/etherscan/model/proxy/BlockProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java similarity index 98% rename from src/main/java/io/api/etherscan/model/proxy/BlockProxy.java rename to src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java index 2afbe40..889cc0e 100644 --- a/src/main/java/io/api/etherscan/model/proxy/BlockProxy.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java @@ -1,7 +1,7 @@ -package io.api.etherscan.model.proxy; +package io.goodforgod.api.etherscan.model.proxy; import com.google.gson.annotations.Expose; -import io.api.etherscan.util.BasicUtils; +import io.goodforgod.api.etherscan.util.BasicUtils; import java.math.BigInteger; import java.time.LocalDateTime; import java.time.ZoneOffset; diff --git a/src/main/java/io/api/etherscan/model/proxy/ReceiptProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java similarity index 96% rename from src/main/java/io/api/etherscan/model/proxy/ReceiptProxy.java rename to src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java index 1e25dbd..f4f7845 100644 --- a/src/main/java/io/api/etherscan/model/proxy/ReceiptProxy.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java @@ -1,8 +1,8 @@ -package io.api.etherscan.model.proxy; +package io.goodforgod.api.etherscan.model.proxy; import com.google.gson.annotations.Expose; -import io.api.etherscan.model.Log; -import io.api.etherscan.util.BasicUtils; +import io.goodforgod.api.etherscan.model.Log; +import io.goodforgod.api.etherscan.util.BasicUtils; import java.math.BigInteger; import java.util.List; diff --git a/src/main/java/io/api/etherscan/model/proxy/TxProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java similarity index 97% rename from src/main/java/io/api/etherscan/model/proxy/TxProxy.java rename to src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java index a89f4a8..cf3199b 100644 --- a/src/main/java/io/api/etherscan/model/proxy/TxProxy.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java @@ -1,7 +1,7 @@ -package io.api.etherscan.model.proxy; +package io.goodforgod.api.etherscan.model.proxy; import com.google.gson.annotations.Expose; -import io.api.etherscan.util.BasicUtils; +import io.goodforgod.api.etherscan.util.BasicUtils; import java.math.BigInteger; /** diff --git a/src/main/java/io/api/etherscan/model/proxy/utility/BaseProxyTO.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BaseProxyTO.java similarity index 86% rename from src/main/java/io/api/etherscan/model/proxy/utility/BaseProxyTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BaseProxyTO.java index 0291dfe..ef57193 100644 --- a/src/main/java/io/api/etherscan/model/proxy/utility/BaseProxyTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BaseProxyTO.java @@ -1,4 +1,4 @@ -package io.api.etherscan.model.proxy.utility; +package io.goodforgod.api.etherscan.model.proxy.utility; /** * @author GoodforGod diff --git a/src/main/java/io/api/etherscan/model/proxy/utility/BlockProxyTO.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BlockProxyTO.java similarity index 63% rename from src/main/java/io/api/etherscan/model/proxy/utility/BlockProxyTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BlockProxyTO.java index 2057c89..cf6c16b 100644 --- a/src/main/java/io/api/etherscan/model/proxy/utility/BlockProxyTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/BlockProxyTO.java @@ -1,6 +1,6 @@ -package io.api.etherscan.model.proxy.utility; +package io.goodforgod.api.etherscan.model.proxy.utility; -import io.api.etherscan.model.proxy.BlockProxy; +import io.goodforgod.api.etherscan.model.proxy.BlockProxy; /** * @author GoodforGod diff --git a/src/main/java/io/api/etherscan/model/proxy/utility/ErrorProxyTO.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/ErrorProxyTO.java similarity index 81% rename from src/main/java/io/api/etherscan/model/proxy/utility/ErrorProxyTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/ErrorProxyTO.java index a3bc435..9b14cec 100644 --- a/src/main/java/io/api/etherscan/model/proxy/utility/ErrorProxyTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/ErrorProxyTO.java @@ -1,4 +1,4 @@ -package io.api.etherscan.model.proxy.utility; +package io.goodforgod.api.etherscan.model.proxy.utility; /** * @author GoodforGod diff --git a/src/main/java/io/api/etherscan/model/proxy/utility/StringProxyTO.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/StringProxyTO.java similarity index 77% rename from src/main/java/io/api/etherscan/model/proxy/utility/StringProxyTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/StringProxyTO.java index 8d1d08c..489d87b 100644 --- a/src/main/java/io/api/etherscan/model/proxy/utility/StringProxyTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/StringProxyTO.java @@ -1,4 +1,4 @@ -package io.api.etherscan.model.proxy.utility; +package io.goodforgod.api.etherscan.model.proxy.utility; /** * @author GoodforGod diff --git a/src/main/java/io/api/etherscan/model/proxy/utility/TxInfoProxyTO.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxInfoProxyTO.java similarity index 63% rename from src/main/java/io/api/etherscan/model/proxy/utility/TxInfoProxyTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxInfoProxyTO.java index 3bbe039..208cdbe 100644 --- a/src/main/java/io/api/etherscan/model/proxy/utility/TxInfoProxyTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxInfoProxyTO.java @@ -1,6 +1,6 @@ -package io.api.etherscan.model.proxy.utility; +package io.goodforgod.api.etherscan.model.proxy.utility; -import io.api.etherscan.model.proxy.ReceiptProxy; +import io.goodforgod.api.etherscan.model.proxy.ReceiptProxy; /** * @author GoodforGod diff --git a/src/main/java/io/api/etherscan/model/proxy/utility/TxProxyTO.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxProxyTO.java similarity index 62% rename from src/main/java/io/api/etherscan/model/proxy/utility/TxProxyTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxProxyTO.java index 7e9c9e8..0c084e7 100644 --- a/src/main/java/io/api/etherscan/model/proxy/utility/TxProxyTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/utility/TxProxyTO.java @@ -1,6 +1,6 @@ -package io.api.etherscan.model.proxy.utility; +package io.goodforgod.api.etherscan.model.proxy.utility; -import io.api.etherscan.model.proxy.TxProxy; +import io.goodforgod.api.etherscan.model.proxy.TxProxy; /** * @author GoodforGod diff --git a/src/main/java/io/api/etherscan/model/query/LogOp.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogOp.java similarity index 86% rename from src/main/java/io/api/etherscan/model/query/LogOp.java rename to src/main/java/io/goodforgod/api/etherscan/model/query/LogOp.java index 0c0ebee..9136034 100644 --- a/src/main/java/io/api/etherscan/model/query/LogOp.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogOp.java @@ -1,4 +1,4 @@ -package io.api.etherscan.model.query; +package io.goodforgod.api.etherscan.model.query; /** * Part of The Event Log API diff --git a/src/main/java/io/goodforgod/api/etherscan/model/query/LogQuery.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQuery.java new file mode 100644 index 0000000..69e8409 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQuery.java @@ -0,0 +1,51 @@ +package io.goodforgod.api.etherscan.model.query; + +import static io.goodforgod.api.etherscan.model.query.LogQueryBuilderImpl.MAX_BLOCK; +import static io.goodforgod.api.etherscan.model.query.LogQueryBuilderImpl.MIN_BLOCK; + +import io.goodforgod.api.etherscan.LogsAPI; +import org.jetbrains.annotations.NotNull; + +/** + * Final builded container for The Event Log API + * EtherScan - API Descriptions ... + * + * @see LogQueryBuilderImpl + * @see LogsAPI + * @author GoodforGod + * @since 10.05.2023 + */ +public interface LogQuery { + + @NotNull + String params(); + + @NotNull + static Builder builder(String address) { + return new LogQueryBuilderImpl(address, MIN_BLOCK, MAX_BLOCK); + } + + interface Builder { + + @NotNull + LogQuery.Builder withBlockFrom(long startBlock); + + @NotNull + LogQuery.Builder withBlockTo(long endBlock); + + @NotNull + LogTopicSingle withTopic(String topic0); + + @NotNull + LogTopicTuple withTopic(String topic0, String topic1); + + @NotNull + LogTopicTriple withTopic(String topic0, String topic1, String topic2); + + @NotNull + LogTopicQuadro withTopic(String topic0, String topic1, String topic2, String topic3); + + @NotNull + LogQuery build(); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryBuilderImpl.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryBuilderImpl.java new file mode 100644 index 0000000..0d1eb59 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryBuilderImpl.java @@ -0,0 +1,84 @@ +package io.goodforgod.api.etherscan.model.query; + +import io.goodforgod.api.etherscan.LogsAPI; +import io.goodforgod.api.etherscan.error.ErtherScanLogQueryException; +import io.goodforgod.api.etherscan.util.BasicUtils; +import org.jetbrains.annotations.NotNull; + +/** + * Builder for The Event Log API + * + * @see LogsAPI + * @author GoodforGod + * @since 31.10.2018 + */ +final class LogQueryBuilderImpl implements LogQuery.Builder { + + static final long MIN_BLOCK = 0; + static final long MAX_BLOCK = 99999999999999999L; + + private final String address; + private final long startBlock, endBlock; + + LogQueryBuilderImpl(String address, long startBlock, long endBlock) { + this.address = address; + this.startBlock = startBlock; + this.endBlock = endBlock; + } + + @Override + public @NotNull LogQuery.Builder withBlockFrom(long startBlock) { + return new LogQueryBuilderImpl(this.address, startBlock, this.endBlock); + } + + @Override + public @NotNull LogQuery.Builder withBlockTo(long endBlock) { + return new LogQueryBuilderImpl(this.address, this.startBlock, endBlock); + } + + @Override + public @NotNull LogTopicSingle withTopic(String topic0) { + if (BasicUtils.isNotHex(topic0)) + throw new ErtherScanLogQueryException("topic0 can not be empty or non hex."); + return new LogTopicSingle(address, startBlock, endBlock, topic0); + } + + @Override + public @NotNull LogTopicTuple withTopic(String topic0, String topic1) { + if (BasicUtils.isNotHex(topic0)) + throw new ErtherScanLogQueryException("topic0 can not be empty or non hex."); + if (BasicUtils.isNotHex(topic1)) + throw new ErtherScanLogQueryException("topic1 can not be empty or non hex."); + return new LogTopicTuple(address, startBlock, endBlock, topic0, topic1); + } + + @Override + public @NotNull LogTopicTriple withTopic(String topic0, String topic1, String topic2) { + if (BasicUtils.isNotHex(topic0)) + throw new ErtherScanLogQueryException("topic0 can not be empty or non hex."); + if (BasicUtils.isNotHex(topic1)) + throw new ErtherScanLogQueryException("topic1 can not be empty or non hex."); + if (BasicUtils.isNotHex(topic2)) + throw new ErtherScanLogQueryException("topic2 can not be empty or non hex."); + return new LogTopicTriple(address, startBlock, endBlock, topic0, topic1, topic2); + } + + @Override + public @NotNull LogTopicQuadro withTopic(String topic0, String topic1, String topic2, String topic3) { + if (BasicUtils.isNotHex(topic0)) + throw new ErtherScanLogQueryException("topic0 can not be empty or non hex."); + if (BasicUtils.isNotHex(topic1)) + throw new ErtherScanLogQueryException("topic1 can not be empty or non hex."); + if (BasicUtils.isNotHex(topic2)) + throw new ErtherScanLogQueryException("topic2 can not be empty or non hex."); + if (BasicUtils.isNotHex(topic3)) + throw new ErtherScanLogQueryException("topic3 can not be empty or non hex."); + + return new LogTopicQuadro(address, startBlock, endBlock, topic0, topic1, topic2, topic3); + } + + @Override + public @NotNull LogQuery build() throws ErtherScanLogQueryException { + return new LogQueryImpl("&address=" + this.address + "&fromBlock=" + this.startBlock + "&toBlock=" + this.endBlock); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryImpl.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryImpl.java new file mode 100644 index 0000000..ef66e72 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryImpl.java @@ -0,0 +1,30 @@ +package io.goodforgod.api.etherscan.model.query; + +import io.goodforgod.api.etherscan.LogsAPI; +import org.jetbrains.annotations.NotNull; + +/** + * Final builded container for The Event Log API + * EtherScan - API Descriptions ... + * + * @see LogQueryBuilderImpl + * @see LogsAPI + * @author GoodforGod + * @since 31.10.2018 + */ +final class LogQueryImpl implements LogQuery { + + /** + * Final request parameter for api call + */ + private final String params; + + LogQueryImpl(String params) { + this.params = params; + } + + @Override + public @NotNull String params() { + return params; + } +} diff --git a/src/main/java/io/api/etherscan/model/query/impl/BaseLogQuery.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryParams.java similarity index 79% rename from src/main/java/io/api/etherscan/model/query/impl/BaseLogQuery.java rename to src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryParams.java index c472f84..ac77ae8 100644 --- a/src/main/java/io/api/etherscan/model/query/impl/BaseLogQuery.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryParams.java @@ -1,16 +1,18 @@ -package io.api.etherscan.model.query.impl; +package io.goodforgod.api.etherscan.model.query; -import io.api.etherscan.core.ILogsApi; +import io.goodforgod.api.etherscan.LogsAPI; /** * Base parameters for The Event Log API builder * - * @see LogQueryBuilder - * @see ILogsApi + * @see LogQueryBuilderImpl + * @see LogsAPI * @author GoodforGod * @since 31.10.2018 */ -abstract class BaseLogQuery { +final class LogQueryParams { + + private LogQueryParams() {} static final String FROM_BLOCK_PARAM = "&fromBlock="; static final String TO_BLOCK_PARAM = "&toBlock="; diff --git a/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicBuilder.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicBuilder.java new file mode 100644 index 0000000..715f869 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicBuilder.java @@ -0,0 +1,17 @@ +package io.goodforgod.api.etherscan.model.query; + +import org.jetbrains.annotations.NotNull; + +/** + * @see LogTopicSingle + * @see LogTopicTuple + * @see LogTopicTriple + * @see LogTopicQuadro + * @author GoodforGod + * @since 10.05.2023 + */ +public interface LogTopicBuilder { + + @NotNull + LogQuery build(); +} diff --git a/src/main/java/io/api/etherscan/model/query/impl/LogTopicQuadro.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicQuadro.java similarity index 71% rename from src/main/java/io/api/etherscan/model/query/impl/LogTopicQuadro.java rename to src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicQuadro.java index bab5b29..1469f97 100644 --- a/src/main/java/io/api/etherscan/model/query/impl/LogTopicQuadro.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicQuadro.java @@ -1,19 +1,20 @@ -package io.api.etherscan.model.query.impl; +package io.goodforgod.api.etherscan.model.query; -import io.api.etherscan.core.ILogsApi; -import io.api.etherscan.error.LogQueryException; -import io.api.etherscan.model.query.IQueryBuilder; -import io.api.etherscan.model.query.LogOp; +import static io.goodforgod.api.etherscan.model.query.LogQueryParams.*; + +import io.goodforgod.api.etherscan.LogsAPI; +import io.goodforgod.api.etherscan.error.ErtherScanLogQueryException; +import org.jetbrains.annotations.NotNull; /** * Quadro topic parameter builder for The Event Log API * - * @see LogQueryBuilder - * @see ILogsApi + * @see LogQueryBuilderImpl + * @see LogsAPI * @author GoodforGod * @since 31.10.2018 */ -public class LogTopicQuadro extends BaseLogQuery implements IQueryBuilder { +public final class LogTopicQuadro implements LogTopicBuilder { private final String address; private final long startBlock, endBlock; @@ -68,21 +69,21 @@ public LogTopicQuadro setOpTopic1_3(LogOp topic1_3_opr) { } @Override - public LogQuery build() { + public @NotNull LogQuery build() { if (topic0_1_opr == null) - throw new LogQueryException("topic0_1_opr can not be null."); + throw new ErtherScanLogQueryException("topic0_1_opr can not be null."); if (topic0_2_opr == null) - throw new LogQueryException("topic0_2_opr can not be null."); + throw new ErtherScanLogQueryException("topic0_2_opr can not be null."); if (topic0_3_opr == null) - throw new LogQueryException("topic0_3_opr can not be null."); + throw new ErtherScanLogQueryException("topic0_3_opr can not be null."); if (topic1_2_opr == null) - throw new LogQueryException("topic1_2_opr can not be null."); + throw new ErtherScanLogQueryException("topic1_2_opr can not be null."); if (topic2_3_opr == null) - throw new LogQueryException("topic2_3_opr can not be null."); + throw new ErtherScanLogQueryException("topic2_3_opr can not be null."); if (topic1_3_opr == null) - throw new LogQueryException("topic1_3_opr can not be null."); + throw new ErtherScanLogQueryException("topic1_3_opr can not be null."); - return new LogQuery(ADDRESS_PARAM + address + return new LogQueryImpl(ADDRESS_PARAM + address + FROM_BLOCK_PARAM + startBlock + TO_BLOCK_PARAM + endBlock + TOPIC_0_PARAM + topic0 + TOPIC_1_PARAM + topic1 diff --git a/src/main/java/io/api/etherscan/model/query/impl/LogTopicSingle.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicSingle.java similarity index 53% rename from src/main/java/io/api/etherscan/model/query/impl/LogTopicSingle.java rename to src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicSingle.java index 83199d9..85bd18c 100644 --- a/src/main/java/io/api/etherscan/model/query/impl/LogTopicSingle.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicSingle.java @@ -1,18 +1,20 @@ -package io.api.etherscan.model.query.impl; +package io.goodforgod.api.etherscan.model.query; -import io.api.etherscan.core.ILogsApi; -import io.api.etherscan.error.LogQueryException; -import io.api.etherscan.model.query.IQueryBuilder; +import static io.goodforgod.api.etherscan.model.query.LogQueryParams.*; + +import io.goodforgod.api.etherscan.LogsAPI; +import io.goodforgod.api.etherscan.error.ErtherScanLogQueryException; +import org.jetbrains.annotations.NotNull; /** * Single topic parameter builder for The Event Log API * - * @see LogQueryBuilder - * @see ILogsApi + * @see LogQueryBuilderImpl + * @see LogsAPI * @author GoodforGod * @since 31.10.2018 */ -public class LogTopicSingle extends BaseLogQuery implements IQueryBuilder { +public final class LogTopicSingle implements LogTopicBuilder { private final String address; private final long startBlock, endBlock; @@ -27,8 +29,8 @@ public class LogTopicSingle extends BaseLogQuery implements IQueryBuilder { } @Override - public LogQuery build() throws LogQueryException { - return new LogQuery(ADDRESS_PARAM + address + public @NotNull LogQuery build() throws ErtherScanLogQueryException { + return new LogQueryImpl(ADDRESS_PARAM + address + FROM_BLOCK_PARAM + startBlock + TO_BLOCK_PARAM + endBlock + TOPIC_0_PARAM + topic0); } diff --git a/src/main/java/io/api/etherscan/model/query/impl/LogTopicTriple.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTriple.java similarity index 68% rename from src/main/java/io/api/etherscan/model/query/impl/LogTopicTriple.java rename to src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTriple.java index cc9a6ba..d56edb5 100644 --- a/src/main/java/io/api/etherscan/model/query/impl/LogTopicTriple.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTriple.java @@ -1,19 +1,20 @@ -package io.api.etherscan.model.query.impl; +package io.goodforgod.api.etherscan.model.query; -import io.api.etherscan.core.ILogsApi; -import io.api.etherscan.error.LogQueryException; -import io.api.etherscan.model.query.IQueryBuilder; -import io.api.etherscan.model.query.LogOp; +import static io.goodforgod.api.etherscan.model.query.LogQueryParams.*; + +import io.goodforgod.api.etherscan.LogsAPI; +import io.goodforgod.api.etherscan.error.ErtherScanLogQueryException; +import org.jetbrains.annotations.NotNull; /** * Triple topic parameter builder for The Event Log API * - * @see LogQueryBuilder - * @see ILogsApi + * @see LogQueryBuilderImpl + * @see LogsAPI * @author GoodforGod * @since 31.10.2018 */ -public class LogTopicTriple extends BaseLogQuery implements IQueryBuilder { +public final class LogTopicTriple implements LogTopicBuilder { private final String address; private final long startBlock, endBlock; @@ -51,15 +52,15 @@ public LogTopicTriple setOpTopic1_2(LogOp topic1_2_opr) { } @Override - public LogQuery build() throws LogQueryException { + public @NotNull LogQuery build() throws ErtherScanLogQueryException { if (topic0_1_opr == null) - throw new LogQueryException("topic0_1_opr can not be null."); + throw new ErtherScanLogQueryException("topic0_1_opr can not be null."); if (topic0_2_opr == null) - throw new LogQueryException("topic0_2_opr can not be null."); + throw new ErtherScanLogQueryException("topic0_2_opr can not be null."); if (topic1_2_opr == null) - throw new LogQueryException("topic1_2_opr can not be null."); + throw new ErtherScanLogQueryException("topic1_2_opr can not be null."); - return new LogQuery(ADDRESS_PARAM + address + return new LogQueryImpl(ADDRESS_PARAM + address + FROM_BLOCK_PARAM + startBlock + TO_BLOCK_PARAM + endBlock + TOPIC_0_PARAM + topic0 + TOPIC_1_PARAM + topic1 diff --git a/src/main/java/io/api/etherscan/model/query/impl/LogTopicTuple.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTuple.java similarity index 63% rename from src/main/java/io/api/etherscan/model/query/impl/LogTopicTuple.java rename to src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTuple.java index 4524a8a..95a78a4 100644 --- a/src/main/java/io/api/etherscan/model/query/impl/LogTopicTuple.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTuple.java @@ -1,19 +1,20 @@ -package io.api.etherscan.model.query.impl; +package io.goodforgod.api.etherscan.model.query; -import io.api.etherscan.core.ILogsApi; -import io.api.etherscan.error.LogQueryException; -import io.api.etherscan.model.query.IQueryBuilder; -import io.api.etherscan.model.query.LogOp; +import static io.goodforgod.api.etherscan.model.query.LogQueryParams.*; + +import io.goodforgod.api.etherscan.LogsAPI; +import io.goodforgod.api.etherscan.error.ErtherScanLogQueryException; +import org.jetbrains.annotations.NotNull; /** * Tuple topic parameter builder for The Event Log API * - * @see LogQueryBuilder - * @see ILogsApi + * @see LogQueryBuilderImpl + * @see LogsAPI * @author GoodforGod * @since 31.10.2018 */ -public class LogTopicTuple extends BaseLogQuery implements IQueryBuilder { +public final class LogTopicTuple implements LogTopicBuilder { private final String address; private final long startBlock, endBlock; @@ -39,11 +40,11 @@ public LogTopicTuple setOpTopic0_1(LogOp topic0_1_opr) { } @Override - public LogQuery build() throws LogQueryException { + public @NotNull LogQuery build() throws ErtherScanLogQueryException { if (topic0_1_opr == null) - throw new LogQueryException("topic0_1_opr can not be null."); + throw new ErtherScanLogQueryException("topic0_1_opr can not be null."); - return new LogQuery(ADDRESS_PARAM + address + return new LogQueryImpl(ADDRESS_PARAM + address + FROM_BLOCK_PARAM + startBlock + TO_BLOCK_PARAM + endBlock + TOPIC_0_PARAM + topic0 + TOPIC_1_PARAM + topic1 diff --git a/src/main/java/io/api/etherscan/model/utility/BalanceResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/BalanceResponseTO.java similarity index 70% rename from src/main/java/io/api/etherscan/model/utility/BalanceResponseTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/response/BalanceResponseTO.java index f7c2985..189ed87 100644 --- a/src/main/java/io/api/etherscan/model/utility/BalanceResponseTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/BalanceResponseTO.java @@ -1,4 +1,4 @@ -package io.api.etherscan.model.utility; +package io.goodforgod.api.etherscan.model.response; /** * @author GoodforGod diff --git a/src/main/java/io/api/etherscan/model/utility/BalanceTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/BalanceTO.java similarity index 83% rename from src/main/java/io/api/etherscan/model/utility/BalanceTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/response/BalanceTO.java index 3956cec..8f56790 100644 --- a/src/main/java/io/api/etherscan/model/utility/BalanceTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/BalanceTO.java @@ -1,4 +1,4 @@ -package io.api.etherscan.model.utility; +package io.goodforgod.api.etherscan.model.response; /** * @author GoodforGod diff --git a/src/main/java/io/api/etherscan/model/utility/BaseListResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/BaseListResponseTO.java similarity index 82% rename from src/main/java/io/api/etherscan/model/utility/BaseListResponseTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/response/BaseListResponseTO.java index 916739e..0d1b06c 100644 --- a/src/main/java/io/api/etherscan/model/utility/BaseListResponseTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/BaseListResponseTO.java @@ -1,4 +1,4 @@ -package io.api.etherscan.model.utility; +package io.goodforgod.api.etherscan.model.response; import java.util.List; diff --git a/src/main/java/io/api/etherscan/model/utility/BaseResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/BaseResponseTO.java similarity index 77% rename from src/main/java/io/api/etherscan/model/utility/BaseResponseTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/response/BaseResponseTO.java index 9679ebb..3e100d1 100644 --- a/src/main/java/io/api/etherscan/model/utility/BaseResponseTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/BaseResponseTO.java @@ -1,6 +1,6 @@ -package io.api.etherscan.model.utility; +package io.goodforgod.api.etherscan.model.response; -import io.api.etherscan.util.BasicUtils; +import io.goodforgod.api.etherscan.util.BasicUtils; /** * @author GoodforGod diff --git a/src/main/java/io/api/etherscan/model/utility/BlockParam.java b/src/main/java/io/goodforgod/api/etherscan/model/response/BlockParam.java similarity index 88% rename from src/main/java/io/api/etherscan/model/utility/BlockParam.java rename to src/main/java/io/goodforgod/api/etherscan/model/response/BlockParam.java index 7e11a00..b2dbd32 100644 --- a/src/main/java/io/api/etherscan/model/utility/BlockParam.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/BlockParam.java @@ -1,4 +1,4 @@ -package io.api.etherscan.model.utility; +package io.goodforgod.api.etherscan.model.response; /** * @author GoodforGod diff --git a/src/main/java/io/api/etherscan/model/utility/BlockResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/BlockResponseTO.java similarity index 54% rename from src/main/java/io/api/etherscan/model/utility/BlockResponseTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/response/BlockResponseTO.java index 8a89321..363612b 100644 --- a/src/main/java/io/api/etherscan/model/utility/BlockResponseTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/BlockResponseTO.java @@ -1,6 +1,6 @@ -package io.api.etherscan.model.utility; +package io.goodforgod.api.etherscan.model.response; -import io.api.etherscan.model.Block; +import io.goodforgod.api.etherscan.model.Block; /** * @author GoodforGod diff --git a/src/main/java/io/api/etherscan/model/utility/LogResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/LogResponseTO.java similarity index 54% rename from src/main/java/io/api/etherscan/model/utility/LogResponseTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/response/LogResponseTO.java index a060bd3..748d155 100644 --- a/src/main/java/io/api/etherscan/model/utility/LogResponseTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/LogResponseTO.java @@ -1,6 +1,6 @@ -package io.api.etherscan.model.utility; +package io.goodforgod.api.etherscan.model.response; -import io.api.etherscan.model.Log; +import io.goodforgod.api.etherscan.model.Log; /** * @author GoodforGod diff --git a/src/main/java/io/api/etherscan/model/utility/PriceResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/PriceResponseTO.java similarity index 66% rename from src/main/java/io/api/etherscan/model/utility/PriceResponseTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/response/PriceResponseTO.java index 9af743b..e2f0b63 100644 --- a/src/main/java/io/api/etherscan/model/utility/PriceResponseTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/PriceResponseTO.java @@ -1,6 +1,6 @@ -package io.api.etherscan.model.utility; +package io.goodforgod.api.etherscan.model.response; -import io.api.etherscan.model.Price; +import io.goodforgod.api.etherscan.model.Price; /** * @author GoodforGod diff --git a/src/main/java/io/api/etherscan/model/utility/ReceiptStatusResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/ReceiptStatusResponseTO.java similarity index 81% rename from src/main/java/io/api/etherscan/model/utility/ReceiptStatusResponseTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/response/ReceiptStatusResponseTO.java index a5f9577..922c5e2 100644 --- a/src/main/java/io/api/etherscan/model/utility/ReceiptStatusResponseTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/ReceiptStatusResponseTO.java @@ -1,4 +1,4 @@ -package io.api.etherscan.model.utility; +package io.goodforgod.api.etherscan.model.response; /** * @author GoodforGod diff --git a/src/main/java/io/api/etherscan/model/utility/ReceiptStatusTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/ReceiptStatusTO.java similarity index 77% rename from src/main/java/io/api/etherscan/model/utility/ReceiptStatusTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/response/ReceiptStatusTO.java index c4c63af..4f4717c 100644 --- a/src/main/java/io/api/etherscan/model/utility/ReceiptStatusTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/ReceiptStatusTO.java @@ -1,4 +1,4 @@ -package io.api.etherscan.model.utility; +package io.goodforgod.api.etherscan.model.response; /** * @author GoodforGod diff --git a/src/main/java/io/api/etherscan/model/utility/StatusResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/StatusResponseTO.java similarity index 66% rename from src/main/java/io/api/etherscan/model/utility/StatusResponseTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/response/StatusResponseTO.java index 7532aba..6847930 100644 --- a/src/main/java/io/api/etherscan/model/utility/StatusResponseTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/StatusResponseTO.java @@ -1,6 +1,6 @@ -package io.api.etherscan.model.utility; +package io.goodforgod.api.etherscan.model.response; -import io.api.etherscan.model.Status; +import io.goodforgod.api.etherscan.model.Status; /** * @author GoodforGod diff --git a/src/main/java/io/api/etherscan/model/utility/StringResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/StringResponseTO.java similarity index 79% rename from src/main/java/io/api/etherscan/model/utility/StringResponseTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/response/StringResponseTO.java index 582087a..4fb9f04 100644 --- a/src/main/java/io/api/etherscan/model/utility/StringResponseTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/StringResponseTO.java @@ -1,4 +1,4 @@ -package io.api.etherscan.model.utility; +package io.goodforgod.api.etherscan.model.response; /** * @author GoodforGod diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/TxERC20ResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/TxERC20ResponseTO.java new file mode 100644 index 0000000..f4814a5 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/TxERC20ResponseTO.java @@ -0,0 +1,11 @@ +package io.goodforgod.api.etherscan.model.response; + +import io.goodforgod.api.etherscan.model.TxERC20; + +/** + * @author GoodforGod + * @since 29.10.2018 + */ +public class TxERC20ResponseTO extends BaseListResponseTO { + +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/TxERC721ResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/TxERC721ResponseTO.java new file mode 100644 index 0000000..b4db8ef --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/TxERC721ResponseTO.java @@ -0,0 +1,11 @@ +package io.goodforgod.api.etherscan.model.response; + +import io.goodforgod.api.etherscan.model.TxERC20; + +/** + * @author GoodforGod + * @since 29.10.2018 + */ +public class TxERC721ResponseTO extends BaseListResponseTO { + +} diff --git a/src/main/java/io/api/etherscan/model/utility/TxInternalResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/TxInternalResponseTO.java similarity index 55% rename from src/main/java/io/api/etherscan/model/utility/TxInternalResponseTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/response/TxInternalResponseTO.java index 5f0e400..efcf4dd 100644 --- a/src/main/java/io/api/etherscan/model/utility/TxInternalResponseTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/TxInternalResponseTO.java @@ -1,6 +1,6 @@ -package io.api.etherscan.model.utility; +package io.goodforgod.api.etherscan.model.response; -import io.api.etherscan.model.TxInternal; +import io.goodforgod.api.etherscan.model.TxInternal; /** * @author GoodforGod diff --git a/src/main/java/io/api/etherscan/model/utility/TxResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/TxResponseTO.java similarity index 54% rename from src/main/java/io/api/etherscan/model/utility/TxResponseTO.java rename to src/main/java/io/goodforgod/api/etherscan/model/response/TxResponseTO.java index 1fa6b16..f4bd3f0 100644 --- a/src/main/java/io/api/etherscan/model/utility/TxResponseTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/TxResponseTO.java @@ -1,6 +1,6 @@ -package io.api.etherscan.model.utility; +package io.goodforgod.api.etherscan.model.response; -import io.api.etherscan.model.Tx; +import io.goodforgod.api.etherscan.model.Tx; /** * @author GoodforGod diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/UncleBlockResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/UncleBlockResponseTO.java new file mode 100644 index 0000000..de94f9e --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/UncleBlockResponseTO.java @@ -0,0 +1,16 @@ +package io.goodforgod.api.etherscan.model.response; + +import io.goodforgod.api.etherscan.model.BlockUncle; + +/** + * @author GoodforGod + * @since 30.10.2018 + */ +public class UncleBlockResponseTO extends BaseResponseTO { + + private BlockUncle result; + + public BlockUncle getResult() { + return result; + } +} diff --git a/src/main/java/io/api/etherscan/util/BasicUtils.java b/src/main/java/io/goodforgod/api/etherscan/util/BasicUtils.java similarity index 80% rename from src/main/java/io/api/etherscan/util/BasicUtils.java rename to src/main/java/io/goodforgod/api/etherscan/util/BasicUtils.java index d748abf..4522ff5 100644 --- a/src/main/java/io/api/etherscan/util/BasicUtils.java +++ b/src/main/java/io/goodforgod/api/etherscan/util/BasicUtils.java @@ -1,10 +1,10 @@ -package io.api.etherscan.util; +package io.goodforgod.api.etherscan.util; -import io.api.etherscan.error.EtherScanException; -import io.api.etherscan.error.InvalidAddressException; -import io.api.etherscan.error.InvalidTxHashException; -import io.api.etherscan.model.utility.BaseResponseTO; -import io.api.etherscan.model.utility.BlockParam; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.error.EtherScanInvalidTxHashException; +import io.goodforgod.api.etherscan.error.EtherScanResponseException; +import io.goodforgod.api.etherscan.model.response.BaseResponseTO; +import io.goodforgod.api.etherscan.model.response.BlockParam; import java.math.BigInteger; import java.util.*; import java.util.regex.Pattern; @@ -16,7 +16,9 @@ * @author GoodforGod * @since 28.10.2018 */ -public class BasicUtils { +public final class BasicUtils { + + private BasicUtils() {} private static final int MAX_END_BLOCK = 999999999; private static final int MIN_START_BLOCK = 0; @@ -25,8 +27,6 @@ public class BasicUtils { private static final Pattern TXHASH_PATTERN = Pattern.compile("0x[a-zA-Z0-9]{64}"); private static final Pattern HEX_PATTERN = Pattern.compile("[a-zA-Z0-9]+"); - private BasicUtils() {} - public static boolean isEmpty(String value) { return value == null || value.isEmpty(); } @@ -78,7 +78,7 @@ public static BigInteger parseHex(String hex) { return BigInteger.valueOf(0); final String formatted = (hex.length() > 2 && hex.charAt(0) == '0' && hex.charAt(1) == 'x') - ? hex.substring(2, hex.length()) + ? hex.substring(2) : hex; return new BigInteger(formatted, 16); @@ -89,24 +89,24 @@ public static BigInteger parseHex(String hex) { public static void validateAddress(String address) { if (isNotAddress(address)) - throw new InvalidAddressException("Address [" + address + "] is not Ethereum based."); + throw new EtherScanInvalidAddressException("Address [" + address + "] is not Ethereum based."); } public static void validateTxHash(String txhash) { if (isNotTxHash(txhash)) - throw new InvalidTxHashException("TxHash [" + txhash + "] is not Ethereum based."); + throw new EtherScanInvalidTxHashException("TxHash [" + txhash + "] is not Ethereum based."); } public static void validateTxResponse(T response) { if (response == null) - throw new EtherScanException("EtherScan responded with null value"); + throw new EtherScanResponseException("EtherScan responded with null value"); if (response.getStatus() != 1) { if (response.getMessage() == null) { - throw new EtherScanException( + throw new EtherScanResponseException( "Unexpected Etherscan exception, no information from server about error, code " + response.getStatus()); } else if (!response.getMessage().startsWith("No tra") && !response.getMessage().startsWith("No rec")) { - throw new EtherScanException(response); + throw new EtherScanResponseException(response); } } } @@ -114,7 +114,7 @@ public static void validateTxResponse(T response) { public static void validateAddresses(List addresses) { for (String address : addresses) { if (isNotAddress(address)) - throw new InvalidAddressException("Address [" + address + "] is not Ethereum based."); + throw new EtherScanInvalidAddressException("Address [" + address + "] is not Ethereum based."); } } diff --git a/src/test/java/io/api/ApiRunner.java b/src/test/java/io/api/ApiRunner.java deleted file mode 100644 index e78ea6d..0000000 --- a/src/test/java/io/api/ApiRunner.java +++ /dev/null @@ -1,60 +0,0 @@ -package io.api; - -import io.api.etherscan.core.impl.EtherScanApi; -import io.api.etherscan.manager.impl.QueueManager; -import io.api.etherscan.model.EthNetwork; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.Assertions; - -public class ApiRunner extends Assertions { - - private static final EtherScanApi api; - private static final EtherScanApi apiRopsten; - private static final EtherScanApi apiRinkeby; - private static final EtherScanApi apiKovan; - private static final String apiKey; - - static { - final String key = System.getenv("API_KEY"); - apiKey = (key == null || key.isEmpty()) - ? EtherScanApi.DEFAULT_KEY - : key; - - final QueueManager queueManager = (EtherScanApi.DEFAULT_KEY.equals(apiKey)) - ? QueueManager.DEFAULT_KEY_QUEUE - : new QueueManager(1, 1200L, 1200L, 0); - - api = new EtherScanApi(ApiRunner.apiKey, EthNetwork.MAINNET, queueManager); - apiKovan = new EtherScanApi(ApiRunner.apiKey, EthNetwork.KOVAN, queueManager); - apiRopsten = new EtherScanApi(ApiRunner.apiKey, EthNetwork.ROPSTEN, queueManager); - apiRinkeby = new EtherScanApi(ApiRunner.apiKey, EthNetwork.RINKEBY, queueManager); - } - - public static String getApiKey() { - return apiKey; - } - - public static EtherScanApi getApi() { - return api; - } - - public static EtherScanApi getApiRopsten() { - return apiRopsten; - } - - public static EtherScanApi getApiRinkeby() { - return apiRinkeby; - } - - public static EtherScanApi getApiKovan() { - return apiKovan; - } - - @AfterAll - public static void cleanup() throws Exception { - api.close(); - apiRopsten.close(); - apiRinkeby.close(); - apiKovan.close(); - } -} diff --git a/src/test/java/io/api/etherscan/EtherScanApiTest.java b/src/test/java/io/api/etherscan/EtherScanApiTest.java deleted file mode 100644 index b649302..0000000 --- a/src/test/java/io/api/etherscan/EtherScanApiTest.java +++ /dev/null @@ -1,81 +0,0 @@ -package io.api.etherscan; - -import io.api.ApiRunner; -import io.api.etherscan.core.impl.EtherScanApi; -import io.api.etherscan.error.ApiException; -import io.api.etherscan.error.ApiKeyException; -import io.api.etherscan.error.ApiTimeoutException; -import io.api.etherscan.executor.IHttpExecutor; -import io.api.etherscan.executor.impl.HttpExecutor; -import io.api.etherscan.model.Balance; -import io.api.etherscan.model.EthNetwork; -import java.util.concurrent.TimeUnit; -import java.util.function.Supplier; -import org.junit.jupiter.api.Test; - -/** - * @author GoodforGod - * @since 05.11.2018 - */ -class EtherScanApiTest extends ApiRunner { - - private final EthNetwork network = EthNetwork.KOVAN; - private final String validKey = "YourKey"; - - @Test - void validKey() { - EtherScanApi api = new EtherScanApi(validKey, network); - assertNotNull(api); - } - - @Test - void emptyKey() { - assertThrows(ApiKeyException.class, () -> new EtherScanApi("")); - } - - @Test - void blankKey() { - assertThrows(ApiKeyException.class, () -> new EtherScanApi(" ", network)); - } - - @Test - void nullNetwork() { - assertThrows(ApiException.class, () -> new EtherScanApi(validKey, null)); - } - - @Test - void noTimeoutOnRead() { - Supplier supplier = () -> new HttpExecutor(300); - EtherScanApi api = new EtherScanApi(EthNetwork.MAINNET, supplier); - Balance balance = api.account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); - assertNotNull(balance); - } - - @Test - void noTimeoutOnReadGroli() { - Supplier supplier = () -> new HttpExecutor(300); - Balance balance = getApi().account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); - assertNotNull(balance); - } - - @Test - void noTimeoutOnReadTobalala() { - Supplier supplier = () -> new HttpExecutor(30000); - Balance balance = getApi().account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); - assertNotNull(balance); - } - - @Test - void noTimeoutUnlimitedAwait() { - Balance balance = getApi().account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); - assertNotNull(balance); - } - - @Test - void timeout() throws InterruptedException { - TimeUnit.SECONDS.sleep(5); - Supplier supplier = () -> new HttpExecutor(300, 300); - EtherScanApi api = new EtherScanApi(getApiKey(), EthNetwork.KOVAN, supplier); - assertThrows(ApiTimeoutException.class, () -> api.account().minedBlocks("0x0010f94b296A852aAac52EA6c5Ac72e03afD032D")); - } -} diff --git a/src/test/java/io/api/manager/QueueManagerTest.java b/src/test/java/io/api/manager/QueueManagerTest.java deleted file mode 100644 index 7bd53a9..0000000 --- a/src/test/java/io/api/manager/QueueManagerTest.java +++ /dev/null @@ -1,55 +0,0 @@ -package io.api.manager; - -import io.api.ApiRunner; -import io.api.etherscan.manager.IQueueManager; -import io.api.etherscan.manager.impl.FakeQueueManager; -import io.api.etherscan.manager.impl.QueueManager; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.Timeout; - -/** - * @author GoodforGod - * @since 03.11.2018 - */ -class QueueManagerTest extends ApiRunner { - - @Test - void fakeManager() { - IQueueManager fakeManager = new FakeQueueManager(); - fakeManager.takeTurn(); - fakeManager.takeTurn(); - fakeManager.takeTurn(); - fakeManager.takeTurn(); - fakeManager.takeTurn(); - fakeManager.takeTurn(); - assertNotNull(fakeManager); - } - - @Test - @Timeout(3500) - void queueManager() { - IQueueManager queueManager = new QueueManager(1, 3); - queueManager.takeTurn(); - queueManager.takeTurn(); - assertNotNull(queueManager); - } - - @Test - @Timeout(4500) - void queueManagerWithDelay() { - IQueueManager queueManager = new QueueManager(1, 2, 2); - queueManager.takeTurn(); - queueManager.takeTurn(); - assertNotNull(queueManager); - } - - @Test - void queueManagerTimeout() { - IQueueManager queueManager = new QueueManager(1, 3); - queueManager.takeTurn(); - long start = System.currentTimeMillis(); - queueManager.takeTurn(); - long end = System.currentTimeMillis(); - assertEquals(3, Math.round((double) (end - start) / 1000)); - } -} diff --git a/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java b/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java new file mode 100644 index 0000000..3b9cbe2 --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java @@ -0,0 +1,66 @@ +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import io.goodforgod.api.etherscan.manager.impl.SemaphoreRequestQueueManager; +import java.time.Duration; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; + +public class ApiRunner extends Assertions { + + private static final String DEFAULT_KEY = "YourApiKeyToken"; + + private static final EtherScanAPI api; + private static final EtherScanAPI apiRopsten; + private static final EtherScanAPI apiRinkeby; + private static final EtherScanAPI apiKovan; + private static final String apiKey; + + static { + final String key = System.getenv("API_KEY"); + apiKey = (key == null || key.isEmpty()) + ? DEFAULT_KEY + : key; + + final RequestQueueManager queueManager = (DEFAULT_KEY.equals(apiKey)) + ? RequestQueueManager.DEFAULT_KEY_QUEUE + : new SemaphoreRequestQueueManager(1, Duration.ofMillis(1200L), Duration.ofMillis(1200L), 0); + + api = EtherScanAPI.builder().withApiKey(ApiRunner.apiKey).withNetwork(EthNetworks.MAINNET).withQueue(queueManager) + .build(); + apiKovan = EtherScanAPI.builder().withApiKey(ApiRunner.apiKey).withNetwork(EthNetworks.KOVAN).withQueue(queueManager) + .build(); + apiRopsten = EtherScanAPI.builder().withApiKey(ApiRunner.apiKey).withNetwork(EthNetworks.ROPSTEN).withQueue(queueManager) + .build(); + apiRinkeby = EtherScanAPI.builder().withApiKey(ApiRunner.apiKey).withNetwork(EthNetworks.RINKEBY).withQueue(queueManager) + .build(); + } + + public static String getApiKey() { + return apiKey; + } + + public static EtherScanAPI getApi() { + return api; + } + + public static EtherScanAPI getApiRopsten() { + return apiRopsten; + } + + public static EtherScanAPI getApiRinkeby() { + return apiRinkeby; + } + + public static EtherScanAPI getApiKovan() { + return apiKovan; + } + + @AfterAll + public static void cleanup() throws Exception { + api.close(); + apiRopsten.close(); + apiRinkeby.close(); + apiKovan.close(); + } +} diff --git a/src/test/java/io/goodforgod/api/etherscan/EtherScanAPITests.java b/src/test/java/io/goodforgod/api/etherscan/EtherScanAPITests.java new file mode 100644 index 0000000..5cc0fe7 --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/EtherScanAPITests.java @@ -0,0 +1,76 @@ +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.error.EtherScanKeyException; +import io.goodforgod.api.etherscan.error.EtherScanTimeoutException; +import io.goodforgod.api.etherscan.executor.EthHttpClient; +import io.goodforgod.api.etherscan.executor.impl.UrlEthHttpClient; +import io.goodforgod.api.etherscan.model.Balance; +import java.time.Duration; +import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; +import org.junit.jupiter.api.Test; + +/** + * @author GoodforGod + * @since 05.11.2018 + */ +class EtherScanAPITests extends ApiRunner { + + private final EthNetworks network = EthNetworks.KOVAN; + private final String validKey = "YourKey"; + + @Test + void validKey() { + EtherScanAPI api = EtherScanAPI.builder().withApiKey(validKey).withNetwork(network).build(); + assertNotNull(api); + } + + @Test + void emptyKey() { + assertThrows(EtherScanKeyException.class, () -> EtherScanAPI.builder().withApiKey("").build()); + } + + @Test + void blankKey() { + assertThrows(EtherScanKeyException.class, + () -> EtherScanAPI.builder().withApiKey(" ").withNetwork(network).build()); + } + + @Test + void noTimeoutOnRead() { + Supplier supplier = () -> new UrlEthHttpClient(Duration.ofMillis(300)); + EtherScanAPI api = EtherScanAPI.builder().withNetwork(EthNetworks.MAINNET).withHttpClient(supplier).build(); + Balance balance = api.account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); + assertNotNull(balance); + } + + @Test + void noTimeoutOnReadGroli() { + Supplier supplier = () -> new UrlEthHttpClient(Duration.ofMillis(300)); + Balance balance = getApi().account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); + assertNotNull(balance); + } + + @Test + void noTimeoutOnReadTobalala() { + Supplier supplier = () -> new UrlEthHttpClient(Duration.ofMillis(30000)); + Balance balance = getApi().account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); + assertNotNull(balance); + } + + @Test + void noTimeoutUnlimitedAwait() { + Balance balance = getApi().account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); + assertNotNull(balance); + } + + @Test + void timeout() throws InterruptedException { + TimeUnit.SECONDS.sleep(5); + Supplier supplier = () -> new UrlEthHttpClient(Duration.ofMillis(300), Duration.ofMillis(300)); + EtherScanAPI api = EtherScanAPI.builder().withApiKey(getApiKey()).withNetwork(EthNetworks.KOVAN).withHttpClient(supplier) + .build(); + assertThrows(EtherScanTimeoutException.class, + () -> api.account().blocksMined("0x0010f94b296A852aAac52EA6c5Ac72e03afD032D")); + } +} diff --git a/src/test/java/io/api/etherscan/account/AccountBalanceListTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceListTest.java similarity index 88% rename from src/test/java/io/api/etherscan/account/AccountBalanceListTest.java rename to src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceListTest.java index 6864175..cd3dac9 100644 --- a/src/test/java/io/api/etherscan/account/AccountBalanceListTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceListTest.java @@ -1,9 +1,9 @@ -package io.api.etherscan.account; +package io.goodforgod.api.etherscan.account; -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidAddressException; -import io.api.etherscan.model.Balance; -import io.api.support.AddressUtil; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.model.Balance; +import io.goodforgod.api.etherscan.support.AddressUtil; import java.math.BigInteger; import java.util.ArrayList; import java.util.List; @@ -61,7 +61,7 @@ void invalidParamWithError() { addresses.add("0x9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9"); addresses.add("C9F32CE1127e44C51cbD182D6364F3D707Fd0d47"); - assertThrows(InvalidAddressException.class, () -> getApi().account().balances(addresses)); + assertThrows(EtherScanInvalidAddressException.class, () -> getApi().account().balances(addresses)); } @Test diff --git a/src/test/java/io/api/etherscan/account/AccountBalanceTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceTest.java similarity index 67% rename from src/test/java/io/api/etherscan/account/AccountBalanceTest.java rename to src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceTest.java index d5427ab..4c06c7c 100644 --- a/src/test/java/io/api/etherscan/account/AccountBalanceTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceTest.java @@ -1,9 +1,9 @@ -package io.api.etherscan.account; +package io.goodforgod.api.etherscan.account; -import io.api.ApiRunner; -import io.api.etherscan.core.impl.EtherScanApi; -import io.api.etherscan.error.InvalidAddressException; -import io.api.etherscan.model.Balance; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.EtherScanAPI; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.model.Balance; import org.junit.jupiter.api.Test; /** @@ -12,7 +12,7 @@ */ class AccountBalanceTest extends ApiRunner { - private final EtherScanApi api = getApi(); + private final EtherScanAPI api = getApi(); @Test void correct() { @@ -29,7 +29,8 @@ void correct() { @Test void invalidParamWithError() { - assertThrows(InvalidAddressException.class, () -> getApi().account().balance("8d4426f94e42f721C7116E81d6688cd935cB3b4F")); + assertThrows(EtherScanInvalidAddressException.class, + () -> getApi().account().balance("8d4426f94e42f721C7116E81d6688cd935cB3b4F")); } @Test diff --git a/src/test/java/io/api/etherscan/account/AccountMinedBlocksTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountMinedBlocksTest.java similarity index 65% rename from src/test/java/io/api/etherscan/account/AccountMinedBlocksTest.java rename to src/test/java/io/goodforgod/api/etherscan/account/AccountMinedBlocksTest.java index ae16174..13d5075 100644 --- a/src/test/java/io/api/etherscan/account/AccountMinedBlocksTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountMinedBlocksTest.java @@ -1,9 +1,9 @@ -package io.api.etherscan.account; +package io.goodforgod.api.etherscan.account; -import io.api.ApiRunner; -import io.api.etherscan.core.impl.EtherScanApi; -import io.api.etherscan.error.InvalidAddressException; -import io.api.etherscan.model.Block; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.EtherScanAPI; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.model.Block; import java.util.List; import org.junit.jupiter.api.Test; @@ -13,11 +13,11 @@ */ class AccountMinedBlocksTest extends ApiRunner { - private final EtherScanApi api = getApi(); + private final EtherScanAPI api = getApi(); @Test void correct() { - List blocks = api.account().minedBlocks("0xE4C6175183029A0f039bf2DFffa5C6e8F3cA9B23"); + List blocks = api.account().blocksMined("0xE4C6175183029A0f039bf2DFffa5C6e8F3cA9B23"); assertNotNull(blocks); assertEquals(223, blocks.size()); @@ -32,13 +32,13 @@ void correct() { @Test void invalidParamWithError() { - assertThrows(InvalidAddressException.class, - () -> getApi().account().minedBlocks("xE4C6175183029A0f039bf2DFffa5C6e8F3cA9B23")); + assertThrows(EtherScanInvalidAddressException.class, + () -> getApi().account().blocksMined("xE4C6175183029A0f039bf2DFffa5C6e8F3cA9B23")); } @Test void correctParamWithEmptyExpectedResult() { - List txs = api.account().minedBlocks("0xE1C6175183029A0f039bf2DFffa5C6e8F3cA9B23"); + List txs = api.account().blocksMined("0xE1C6175183029A0f039bf2DFffa5C6e8F3cA9B23"); assertNotNull(txs); assertTrue(txs.isEmpty()); } diff --git a/src/test/java/io/api/etherscan/account/AccountTokenBalanceTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTokenBalanceTest.java similarity index 63% rename from src/test/java/io/api/etherscan/account/AccountTokenBalanceTest.java rename to src/test/java/io/goodforgod/api/etherscan/account/AccountTokenBalanceTest.java index b8b8146..4df75f3 100644 --- a/src/test/java/io/api/etherscan/account/AccountTokenBalanceTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTokenBalanceTest.java @@ -1,9 +1,9 @@ -package io.api.etherscan.account; +package io.goodforgod.api.etherscan.account; -import io.api.ApiRunner; -import io.api.etherscan.core.impl.EtherScanApi; -import io.api.etherscan.error.InvalidAddressException; -import io.api.etherscan.model.TokenBalance; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.EtherScanAPI; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.model.TokenBalance; import org.junit.jupiter.api.Test; /** @@ -12,7 +12,7 @@ */ class AccountTokenBalanceTest extends ApiRunner { - private final EtherScanApi api = getApi(); + private final EtherScanAPI api = getApi(); @Test void correct() { @@ -31,14 +31,16 @@ void correct() { @Test void invalidAddressParamWithError() { - assertThrows(InvalidAddressException.class, () -> api.account().balance("0x5807e7F124EC2103a59c5249187f772c0b8D6b2", - "0x5EaC95ad5b287cF44E058dCf694419333b796123")); + assertThrows(EtherScanInvalidAddressException.class, + () -> api.account().balance("0x5807e7F124EC2103a59c5249187f772c0b8D6b2", + "0x5EaC95ad5b287cF44E058dCf694419333b796123")); } @Test void invalidContractParamWithError() { - assertThrows(InvalidAddressException.class, () -> api.account().balance("0x5d807e7F124EC2103a59c5249187f772c0b8D6b2", - "0xEaC95ad5b287cF44E058dCf694419333b796123")); + assertThrows(EtherScanInvalidAddressException.class, + () -> api.account().balance("0x5d807e7F124EC2103a59c5249187f772c0b8D6b2", + "0xEaC95ad5b287cF44E058dCf694419333b796123")); } @Test diff --git a/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxERC20Test.java similarity index 72% rename from src/test/java/io/api/etherscan/account/AccountTxTokenTest.java rename to src/test/java/io/goodforgod/api/etherscan/account/AccountTxERC20Test.java index 044991b..bacf2e3 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxTokenTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxERC20Test.java @@ -1,8 +1,8 @@ -package io.api.etherscan.account; +package io.goodforgod.api.etherscan.account; -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidAddressException; -import io.api.etherscan.model.TxToken; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.model.TxERC20; import java.util.List; import org.junit.jupiter.api.Test; @@ -10,11 +10,11 @@ * @author GoodforGod * @since 03.11.2018 */ -class AccountTxTokenTest extends ApiRunner { +class AccountTxERC20Test extends ApiRunner { @Test void correct() { - List txs = getApi().account().txsToken("0xE376F69ED2218076682e2b3B7b9099eC50aD68c4"); + List txs = getApi().account().txsERC20("0xE376F69ED2218076682e2b3B7b9099eC50aD68c4"); assertNotNull(txs); assertEquals(3, txs.size()); assertTxs(txs); @@ -33,7 +33,7 @@ void correct() { @Test void correctStartBlock() { - List txs = getApi().account().txsToken("0x36ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7", 5578167); + List txs = getApi().account().txsERC20("0x36ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7", 5578167); assertNotNull(txs); assertEquals(11, txs.size()); assertTxs(txs); @@ -41,7 +41,7 @@ void correctStartBlock() { @Test void correctStartBlockEndBlock() { - List txs = getApi().account().txsToken("0x36ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7", 5578167, 5813576); + List txs = getApi().account().txsERC20("0x36ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7", 5578167, 5813576); assertNotNull(txs); assertEquals(5, txs.size()); assertTxs(txs); @@ -49,19 +49,19 @@ void correctStartBlockEndBlock() { @Test void invalidParamWithError() { - assertThrows(InvalidAddressException.class, - () -> getApi().account().txsToken("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7")); + assertThrows(EtherScanInvalidAddressException.class, + () -> getApi().account().txsERC20("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7")); } @Test void correctParamWithEmptyExpectedResult() { - List txs = getApi().account().txsToken("0x31ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); + List txs = getApi().account().txsERC20("0x31ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); assertNotNull(txs); assertTrue(txs.isEmpty()); } - private void assertTxs(List txs) { - for (TxToken tx : txs) { + private void assertTxs(List txs) { + for (TxERC20 tx : txs) { assertNotNull(tx.getBlockHash()); assertNotNull(tx.getTokenName()); assertNotNull(tx.getTokenSymbol()); diff --git a/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalByHashTest.java similarity index 81% rename from src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java rename to src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalByHashTest.java index 4e63dbc..13036bc 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxInternalByHashTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalByHashTest.java @@ -1,10 +1,10 @@ -package io.api.etherscan.account; +package io.goodforgod.api.etherscan.account; -import io.api.ApiRunner; -import io.api.etherscan.core.impl.EtherScanApi; -import io.api.etherscan.error.InvalidTxHashException; -import io.api.etherscan.model.TxInternal; -import io.api.etherscan.util.BasicUtils; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.EtherScanAPI; +import io.goodforgod.api.etherscan.error.EtherScanInvalidTxHashException; +import io.goodforgod.api.etherscan.model.TxInternal; +import io.goodforgod.api.etherscan.util.BasicUtils; import java.util.List; import org.junit.jupiter.api.Test; @@ -14,7 +14,7 @@ */ class AccountTxInternalByHashTest extends ApiRunner { - private final EtherScanApi api = getApi(); + private final EtherScanAPI api = getApi(); @Test void correct() { @@ -42,7 +42,7 @@ void correct() { @Test void invalidParamWithError() { - assertThrows(InvalidTxHashException.class, + assertThrows(EtherScanInvalidTxHashException.class, () -> api.account().txsInternalByHash("0xb513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b")); } diff --git a/src/test/java/io/api/etherscan/account/AccountTxInternalTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalTest.java similarity index 86% rename from src/test/java/io/api/etherscan/account/AccountTxInternalTest.java rename to src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalTest.java index 7144671..6fb92b4 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxInternalTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalTest.java @@ -1,8 +1,8 @@ -package io.api.etherscan.account; +package io.goodforgod.api.etherscan.account; -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidAddressException; -import io.api.etherscan.model.TxInternal; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.model.TxInternal; import java.util.List; import org.junit.jupiter.api.Test; @@ -41,7 +41,7 @@ void correctStartBlockEndBlock() { @Test void invalidParamWithError() { - assertThrows(InvalidAddressException.class, + assertThrows(EtherScanInvalidAddressException.class, () -> getApi().account().txsInternal("0x2C1ba59D6F58433FB1EaEe7d20b26Ed83bDA51")); } diff --git a/src/test/java/io/api/etherscan/account/AccountTxRc721TokenTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTest.java similarity index 65% rename from src/test/java/io/api/etherscan/account/AccountTxRc721TokenTest.java rename to src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTest.java index 6601d1a..c85aaa4 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxRc721TokenTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTest.java @@ -1,9 +1,11 @@ -package io.api.etherscan.account; +package io.goodforgod.api.etherscan.account; -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidAddressException; -import io.api.etherscan.model.TxToken; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.model.TxERC20; import java.util.List; + +import io.goodforgod.api.etherscan.model.TxERC721; import org.junit.jupiter.api.Test; /** @@ -14,7 +16,7 @@ class AccountTxRc721TokenTest extends ApiRunner { @Test void correct() { - List txs = getApi().account().txsNftToken("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67"); + List txs = getApi().account().txsERC721("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67"); assertNotNull(txs); assertEquals(16, txs.size()); assertTxs(txs); @@ -33,7 +35,7 @@ void correct() { @Test void correctStartBlock() { - List txs = getApi().account().txsNftToken("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67", 4762071); + List txs = getApi().account().txsERC721("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67", 4762071); System.out.println(txs); assertNotNull(txs); assertEquals(5, txs.size()); @@ -42,7 +44,7 @@ void correctStartBlock() { @Test void correctStartBlockEndBlock() { - List txs = getApi().account().txsNftToken("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67", 4761862, 4761934); + List txs = getApi().account().txsERC721("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67", 4761862, 4761934); System.out.println(txs); assertNotNull(txs); assertEquals(11, txs.size()); @@ -51,19 +53,19 @@ void correctStartBlockEndBlock() { @Test void invalidParamWithError() { - assertThrows(InvalidAddressException.class, - () -> getApi().account().txsNftToken("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7")); + assertThrows(EtherScanInvalidAddressException.class, + () -> getApi().account().txsERC721("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7")); } @Test void correctParamWithEmptyExpectedResult() { - List txs = getApi().account().txsNftToken("0x31ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); + List txs = getApi().account().txsERC721("0x31ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); assertNotNull(txs); assertTrue(txs.isEmpty()); } - private void assertTxs(List txs) { - for (TxToken tx : txs) { + private void assertTxs(List txs) { + for (TxERC721 tx : txs) { assertNotNull(tx.getBlockHash()); assertNotNull(tx.getTokenName()); assertNotNull(tx.getTokenSymbol()); diff --git a/src/test/java/io/api/etherscan/account/AccountTxsTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxsTest.java similarity index 87% rename from src/test/java/io/api/etherscan/account/AccountTxsTest.java rename to src/test/java/io/goodforgod/api/etherscan/account/AccountTxsTest.java index 899d0fb..a2cffd1 100644 --- a/src/test/java/io/api/etherscan/account/AccountTxsTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxsTest.java @@ -1,8 +1,8 @@ -package io.api.etherscan.account; +package io.goodforgod.api.etherscan.account; -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidAddressException; -import io.api.etherscan.model.Tx; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.model.Tx; import java.util.List; import org.junit.jupiter.api.Test; @@ -54,7 +54,8 @@ void correctStartBlockEndBlock() { @Test void invalidParamWithError() { - assertThrows(InvalidAddressException.class, () -> getApi().account().txs("9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9")); + assertThrows(EtherScanInvalidAddressException.class, + () -> getApi().account().txs("9327cb34984c3992ec1EA0eAE98Ccf80A74f95B9")); } @Test diff --git a/src/test/java/io/api/etherscan/block/BlockApiTest.java b/src/test/java/io/goodforgod/api/etherscan/block/BlockApiTest.java similarity index 82% rename from src/test/java/io/api/etherscan/block/BlockApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/block/BlockApiTest.java index cee8bb9..8e3b529 100644 --- a/src/test/java/io/api/etherscan/block/BlockApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/block/BlockApiTest.java @@ -1,7 +1,7 @@ -package io.api.etherscan.block; +package io.goodforgod.api.etherscan.block; -import io.api.ApiRunner; -import io.api.etherscan.model.UncleBlock; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.model.BlockUncle; import java.util.Optional; import org.junit.jupiter.api.Test; @@ -13,7 +13,7 @@ class BlockApiTest extends ApiRunner { @Test void correct() { - Optional uncle = getApi().block().uncles(2165403); + Optional uncle = getApi().block().uncles(2165403); assertTrue(uncle.isPresent()); assertFalse(uncle.get().isEmpty()); assertNotNull(uncle.get().getBlockMiner()); @@ -25,7 +25,7 @@ void correct() { assertNotEquals(-1, uncle.get().getUncles().get(0).getUnclePosition()); assertNotNull(uncle.get().toString()); - UncleBlock empty = new UncleBlock(); + BlockUncle empty = new BlockUncle(); assertNotEquals(uncle.get().hashCode(), empty.hashCode()); assertNotEquals(uncle.get(), empty); assertTrue(empty.isEmpty()); @@ -44,14 +44,14 @@ void correct() { @Test void correctNoUncles() { - Optional uncles = getApi().block().uncles(34); + Optional uncles = getApi().block().uncles(34); assertTrue(uncles.isPresent()); assertTrue(uncles.get().getUncles().isEmpty()); } @Test void correctParamWithEmptyExpectedResult() { - Optional uncles = getApi().block().uncles(99999999934L); + Optional uncles = getApi().block().uncles(99999999934L); assertFalse(uncles.isPresent()); } } diff --git a/src/test/java/io/api/etherscan/contract/ContractApiTest.java b/src/test/java/io/goodforgod/api/etherscan/contract/ContractApiTest.java similarity index 78% rename from src/test/java/io/api/etherscan/contract/ContractApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/contract/ContractApiTest.java index 85fb905..62aa7da 100644 --- a/src/test/java/io/api/etherscan/contract/ContractApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/contract/ContractApiTest.java @@ -1,8 +1,8 @@ -package io.api.etherscan.contract; +package io.goodforgod.api.etherscan.contract; -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidAddressException; -import io.api.etherscan.model.Abi; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.model.Abi; import org.junit.jupiter.api.Test; /** @@ -27,7 +27,7 @@ void correct() { @Test void invalidParamWithError() { - assertThrows(InvalidAddressException.class, + assertThrows(EtherScanInvalidAddressException.class, () -> getApi().contract().contractAbi("0xBBbc244D798123fDe783fCc1C72d3Bb8C189413")); } diff --git a/src/test/java/io/api/etherscan/logs/LogQueryBuilderTest.java b/src/test/java/io/goodforgod/api/etherscan/logs/LogQueryBuilderTest.java similarity index 59% rename from src/test/java/io/api/etherscan/logs/LogQueryBuilderTest.java rename to src/test/java/io/goodforgod/api/etherscan/logs/LogQueryBuilderTest.java index f956364..752c34c 100644 --- a/src/test/java/io/api/etherscan/logs/LogQueryBuilderTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/logs/LogQueryBuilderTest.java @@ -1,12 +1,9 @@ -package io.api.etherscan.logs; - -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidAddressException; -import io.api.etherscan.error.LogQueryException; -import io.api.etherscan.model.query.LogOp; -import io.api.etherscan.model.query.impl.LogQuery; -import io.api.etherscan.model.query.impl.LogQueryBuilder; -import io.api.etherscan.model.query.impl.LogTopicQuadro; +package io.goodforgod.api.etherscan.logs; + +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.ErtherScanLogQueryException; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.model.query.*; import org.junit.jupiter.api.Test; /** @@ -17,53 +14,55 @@ class LogQueryBuilderTest extends ApiRunner { @Test void singleCorrect() { - LogQuery single = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") + LogQuery single = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") .build(); assertNotNull(single); - assertNotNull(single.getParams()); + assertNotNull(single.params()); } @Test void singleInCorrectAddress() { - assertThrows(InvalidAddressException.class, () -> LogQueryBuilder.with("033990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") - .build()); + assertThrows(EtherScanInvalidAddressException.class, + () -> LogQuery.builder("033990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") + .build()); } @Test void singleInCorrectTopic() { - assertThrows(LogQueryException.class, () -> LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("6516=") + assertThrows(ErtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("6516=") .build()); } @Test void tupleCorrect() { - LogQuery tuple = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224) - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + LogQuery tuple = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c").withBlockFrom(379224) + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000") .setOpTopic0_1(LogOp.AND) .build(); assertNotNull(tuple); - assertNotNull(tuple.getParams()); + assertNotNull(tuple.params()); } @Test void tupleInCorrectOp() { - assertThrows(LogQueryException.class, () -> LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224) - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - "0x72657075746174696f6e00000000000000000000000000000000000000000000") - .setOpTopic0_1(null) - .build()); + assertThrows(ErtherScanLogQueryException.class, + () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c").withBlockFrom(379224) + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0x72657075746174696f6e00000000000000000000000000000000000000000000") + .setOpTopic0_1(null) + .build()); } @Test void tripleCorrect() { - LogQuery triple = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + LogQuery triple = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c").withBlockFrom(379224).withBlockTo(400000) + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "0x72657075746174696f6e00000000000000000000000000000000000000000000") .setOpTopic0_1(LogOp.AND) @@ -72,14 +71,14 @@ void tripleCorrect() { .build(); assertNotNull(triple); - assertNotNull(triple.getParams()); + assertNotNull(triple.params()); } @Test void tripleInCorrectOp() { - assertThrows(LogQueryException.class, - () -> LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + assertThrows(ErtherScanLogQueryException.class, + () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c").withBlockFrom(379224).withBlockTo(400000) + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "0x72657075746174696f6e00000000000000000000000000000000000000000000") .setOpTopic0_1(LogOp.AND) @@ -90,9 +89,9 @@ void tripleInCorrectOp() { @Test void tripleInCorrectTopic1() { - assertThrows(LogQueryException.class, - () -> LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) - .topic(null, + assertThrows(ErtherScanLogQueryException.class, + () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c").withBlockFrom(379224).withBlockTo(400000) + .withTopic(null, "0x72657075746174696f6e00000000000000000000000000000000000000000000", "0x72657075746174696f6e00000000000000000000000000000000000000000000") .setOpTopic0_1(LogOp.AND) @@ -103,9 +102,9 @@ void tripleInCorrectTopic1() { @Test void tripleInCorrectTopic2() { - assertThrows(LogQueryException.class, - () -> LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + assertThrows(ErtherScanLogQueryException.class, + () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c").withBlockFrom(379224).withBlockTo(400000) + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", null, "0x72657075746174696f6e00000000000000000000000000000000000000000000") .setOpTopic0_1(LogOp.AND) @@ -116,9 +115,9 @@ void tripleInCorrectTopic2() { @Test void tripleInCorrectTopic3() { - assertThrows(LogQueryException.class, - () -> LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + assertThrows(ErtherScanLogQueryException.class, + () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c").withBlockFrom(379224).withBlockTo(400000) + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", null) .setOpTopic0_1(LogOp.AND) @@ -129,8 +128,8 @@ void tripleInCorrectTopic3() { @Test void quadroCorrect() { - LogQuery quadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + LogQuery quadro = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "0x72657075746174696f6e00000000000000000000000000000000000000000000") @@ -143,13 +142,13 @@ void quadroCorrect() { .build(); assertNotNull(quadro); - assertNotNull(quadro.getParams()); + assertNotNull(quadro.params()); } @Test void quadroIncorrectTopic2() { - assertThrows(LogQueryException.class, () -> LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + assertThrows(ErtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", null, "0x72657075746174696f6e00000000000000000000000000000000000000000000", "0x72657075746174696f6e00000000000000000000000000000000000000000000") @@ -164,8 +163,8 @@ void quadroIncorrectTopic2() { @Test void tupleIncorrectTopic2() { - assertThrows(LogQueryException.class, () -> LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + assertThrows(ErtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", null) .setOpTopic0_1(LogOp.AND) .build()); @@ -173,8 +172,8 @@ void tupleIncorrectTopic2() { @Test void tupleIncorrectTopic1() { - assertThrows(LogQueryException.class, () -> LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic(null, + assertThrows(ErtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic(null, "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") .setOpTopic0_1(LogOp.AND) .build()); @@ -182,13 +181,13 @@ void tupleIncorrectTopic1() { @Test void quadroIncorrectOp1() { - final LogTopicQuadro topicQuadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + final LogTopicQuadro topicQuadro = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "0x72657075746174696f6e00000000000000000000000000000000000000000000"); - assertThrows(LogQueryException.class, () -> topicQuadro + assertThrows(ErtherScanLogQueryException.class, () -> topicQuadro .setOpTopic0_1(null) .setOpTopic0_2(LogOp.OR) .setOpTopic0_3(LogOp.AND) @@ -200,13 +199,13 @@ void quadroIncorrectOp1() { @Test void quadroIncorrectOp2() { - final LogTopicQuadro topicQuadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + final LogTopicQuadro topicQuadro = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "0x72657075746174696f6e00000000000000000000000000000000000000000000"); - assertThrows(LogQueryException.class, () -> topicQuadro.setOpTopic0_1(LogOp.AND) + assertThrows(ErtherScanLogQueryException.class, () -> topicQuadro.setOpTopic0_1(LogOp.AND) .setOpTopic0_2(null) .setOpTopic0_3(LogOp.AND) .setOpTopic1_2(LogOp.OR) @@ -217,13 +216,13 @@ void quadroIncorrectOp2() { @Test void quadroIncorrectOp3() { - final LogTopicQuadro topicQuadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + final LogTopicQuadro topicQuadro = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "0x72657075746174696f6e00000000000000000000000000000000000000000000"); - assertThrows(LogQueryException.class, () -> topicQuadro + assertThrows(ErtherScanLogQueryException.class, () -> topicQuadro .setOpTopic0_1(LogOp.AND) .setOpTopic0_2(LogOp.OR) .setOpTopic0_3(null) @@ -235,8 +234,8 @@ void quadroIncorrectOp3() { @Test void quadroInCorrectAgainTopic() { - assertThrows(LogQueryException.class, () -> LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + assertThrows(ErtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "0x72657075746174696f6e00000000000000000000000000000000000000000000", null) @@ -251,13 +250,13 @@ void quadroInCorrectAgainTopic() { @Test void quadroInCorrectOp4() { - final LogTopicQuadro topicQuadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + final LogTopicQuadro topicQuadro = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545"); - assertThrows(LogQueryException.class, () -> topicQuadro + assertThrows(ErtherScanLogQueryException.class, () -> topicQuadro .setOpTopic0_1(LogOp.AND) .setOpTopic0_2(LogOp.OR) .setOpTopic0_3(LogOp.AND) @@ -269,13 +268,13 @@ void quadroInCorrectOp4() { @Test void quadroInCorrectOp5() { - final LogTopicQuadro topicQuadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + final LogTopicQuadro topicQuadro = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545"); - assertThrows(LogQueryException.class, () -> topicQuadro + assertThrows(ErtherScanLogQueryException.class, () -> topicQuadro .setOpTopic0_1(LogOp.AND) .setOpTopic0_2(LogOp.OR) .setOpTopic0_3(LogOp.AND) @@ -287,13 +286,13 @@ void quadroInCorrectOp5() { @Test void quadroInCorrectOp6() { - LogTopicQuadro topicQuadro = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + LogTopicQuadro topicQuadro = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545"); - assertThrows(LogQueryException.class, () -> topicQuadro + assertThrows(ErtherScanLogQueryException.class, () -> topicQuadro .setOpTopic0_1(LogOp.AND) .setOpTopic0_2(LogOp.OR) .setOpTopic0_3(LogOp.AND) @@ -305,8 +304,8 @@ void quadroInCorrectOp6() { @Test void quadroInCorrectTopic() { - assertThrows(LogQueryException.class, () -> LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + assertThrows(ErtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "", "") diff --git a/src/test/java/io/api/etherscan/logs/LogsApiTest.java b/src/test/java/io/goodforgod/api/etherscan/logs/LogsApiTest.java similarity index 67% rename from src/test/java/io/api/etherscan/logs/LogsApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/logs/LogsApiTest.java index e786e0c..0f90d37 100644 --- a/src/test/java/io/api/etherscan/logs/LogsApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/logs/LogsApiTest.java @@ -1,10 +1,9 @@ -package io.api.etherscan.logs; +package io.goodforgod.api.etherscan.logs; -import io.api.ApiRunner; -import io.api.etherscan.model.Log; -import io.api.etherscan.model.query.LogOp; -import io.api.etherscan.model.query.impl.LogQuery; -import io.api.etherscan.model.query.impl.LogQueryBuilder; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.model.Log; +import io.goodforgod.api.etherscan.model.query.LogOp; +import io.goodforgod.api.etherscan.model.query.LogQuery; import java.util.List; import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; @@ -18,22 +17,22 @@ class LogsApiTest extends ApiRunner { static Stream source() { - LogQuery single = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") + LogQuery single = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") .build(); - LogQuery singleInvalidAddr = LogQueryBuilder.with("0x13990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") + LogQuery singleInvalidAddr = LogQuery.builder("0x13990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") .build(); - LogQuery tupleAnd = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224) - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + LogQuery tupleAnd = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c").withBlockFrom(379224) + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000") .setOpTopic0_1(LogOp.AND) .build(); - LogQuery tupleOr = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + LogQuery tupleOr = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000") .setOpTopic0_1(LogOp.OR) .build(); diff --git a/src/test/java/io/goodforgod/api/etherscan/manager/SemaphoreRequestQueueManagerTest.java b/src/test/java/io/goodforgod/api/etherscan/manager/SemaphoreRequestQueueManagerTest.java new file mode 100644 index 0000000..23d14a2 --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/manager/SemaphoreRequestQueueManagerTest.java @@ -0,0 +1,56 @@ +package io.goodforgod.api.etherscan.manager; + +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.manager.impl.FakeRequestQueueManager; +import io.goodforgod.api.etherscan.manager.impl.SemaphoreRequestQueueManager; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; + +import java.time.Duration; + +/** + * @author GoodforGod + * @since 03.11.2018 + */ +class SemaphoreRequestQueueManagerTest extends ApiRunner { + + @Test + void fakeManager() { + RequestQueueManager fakeManager = new FakeRequestQueueManager(); + fakeManager.takeTurn(); + fakeManager.takeTurn(); + fakeManager.takeTurn(); + fakeManager.takeTurn(); + fakeManager.takeTurn(); + fakeManager.takeTurn(); + assertNotNull(fakeManager); + } + + @Test + @Timeout(3500) + void queueManager() { + RequestQueueManager requestQueueManager = new SemaphoreRequestQueueManager(1, Duration.ofSeconds(3)); + requestQueueManager.takeTurn(); + requestQueueManager.takeTurn(); + assertNotNull(requestQueueManager); + } + + @Test + @Timeout(4500) + void queueManagerWithDelay() { + RequestQueueManager requestQueueManager = new SemaphoreRequestQueueManager(1, Duration.ofSeconds(2), Duration.ofSeconds(2)); + requestQueueManager.takeTurn(); + requestQueueManager.takeTurn(); + assertNotNull(requestQueueManager); + } + + @Test + void queueManagerTimeout() { + RequestQueueManager requestQueueManager = new SemaphoreRequestQueueManager(1, Duration.ofSeconds(3)); + requestQueueManager.takeTurn(); + long start = System.currentTimeMillis(); + requestQueueManager.takeTurn(); + long end = System.currentTimeMillis(); + assertEquals(3, Math.round((double) (end - start) / 1000)); + } +} diff --git a/src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTest.java similarity index 76% rename from src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTest.java index faead19..64e3fe6 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyBlockApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTest.java @@ -1,10 +1,10 @@ -package io.api.etherscan.proxy; +package io.goodforgod.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 io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.EthNetworks; +import io.goodforgod.api.etherscan.EtherScanAPI; +import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import io.goodforgod.api.etherscan.model.proxy.BlockProxy; import java.util.Optional; import org.junit.jupiter.api.Test; @@ -14,11 +14,12 @@ */ class ProxyBlockApiTest extends ApiRunner { - private final EtherScanApi api; + private final EtherScanAPI api; ProxyBlockApiTest() { - final QueueManager queueManager = new QueueManager(1, 5100L, 5100L, 0); - this.api = new EtherScanApi(getApiKey(), EthNetwork.MAINNET, queueManager); + final RequestQueueManager queueManager = RequestQueueManager.DEFAULT_KEY_QUEUE; + this.api = EtherScanAPI.builder().withApiKey(getApiKey()).withNetwork(EthNetworks.MAINNET).withQueue(queueManager) + .build(); } @Test diff --git a/src/test/java/io/api/etherscan/proxy/ProxyBlockLastNoApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockLastNoApiTest.java similarity index 75% rename from src/test/java/io/api/etherscan/proxy/ProxyBlockLastNoApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockLastNoApiTest.java index f866b6a..c4f5e31 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyBlockLastNoApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockLastNoApiTest.java @@ -1,6 +1,6 @@ -package io.api.etherscan.proxy; +package io.goodforgod.api.etherscan.proxy; -import io.api.ApiRunner; +import io.goodforgod.api.etherscan.ApiRunner; import org.junit.jupiter.api.Test; /** diff --git a/src/test/java/io/api/etherscan/proxy/ProxyBlockUncleApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockUncleApiTest.java similarity index 83% rename from src/test/java/io/api/etherscan/proxy/ProxyBlockUncleApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockUncleApiTest.java index 67a8875..c575072 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyBlockUncleApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockUncleApiTest.java @@ -1,7 +1,7 @@ -package io.api.etherscan.proxy; +package io.goodforgod.api.etherscan.proxy; -import io.api.ApiRunner; -import io.api.etherscan.model.proxy.BlockProxy; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.model.proxy.BlockProxy; import java.util.Optional; import org.junit.jupiter.api.Test; diff --git a/src/test/java/io/api/etherscan/proxy/ProxyCallApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCallApiTest.java similarity index 54% rename from src/test/java/io/api/etherscan/proxy/ProxyCallApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCallApiTest.java index 8cf46c9..67e7682 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyCallApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCallApiTest.java @@ -1,9 +1,9 @@ -package io.api.etherscan.proxy; +package io.goodforgod.api.etherscan.proxy; -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidAddressException; -import io.api.etherscan.error.InvalidDataHexException; -import io.api.etherscan.util.BasicUtils; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.error.EtherScanInvalidDataHexException; +import io.goodforgod.api.etherscan.util.BasicUtils; import java.util.Optional; import org.junit.jupiter.api.Test; @@ -23,14 +23,16 @@ void correct() { @Test void invalidParamWithError() { - assertThrows(InvalidAddressException.class, () -> getApi().proxy().call("0xEEF46DB4855E25702F8237E8f403FddcaF931C0", - "0x70a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724")); + assertThrows(EtherScanInvalidAddressException.class, + () -> getApi().proxy().call("0xEEF46DB4855E25702F8237E8f403FddcaF931C0", + "0x70a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724")); } @Test void invalidParamNotHex() { - assertThrows(InvalidDataHexException.class, () -> getApi().proxy().call("0xAEEF46DB4855E25702F8237E8f403FddcaF931C0", - "7-0a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724")); + assertThrows(EtherScanInvalidDataHexException.class, + () -> getApi().proxy().call("0xAEEF46DB4855E25702F8237E8f403FddcaF931C0", + "7-0a08231000000000000000000000000e16359506c028e51f16be38986ec5746251e9724")); } @Test diff --git a/src/test/java/io/api/etherscan/proxy/ProxyCodeApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCodeApiTest.java similarity index 66% rename from src/test/java/io/api/etherscan/proxy/ProxyCodeApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCodeApiTest.java index 6835f07..c9dab25 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyCodeApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCodeApiTest.java @@ -1,8 +1,8 @@ -package io.api.etherscan.proxy; +package io.goodforgod.api.etherscan.proxy; -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidAddressException; -import io.api.etherscan.util.BasicUtils; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.util.BasicUtils; import java.util.Optional; import org.junit.jupiter.api.Test; @@ -21,7 +21,8 @@ void correct() { @Test void invalidParamWithError() { - assertThrows(InvalidAddressException.class, () -> getApi().proxy().code("0f75e354c5edc8efed9b59ee9f67a80845ade7d0c")); + assertThrows(EtherScanInvalidAddressException.class, + () -> getApi().proxy().code("0f75e354c5edc8efed9b59ee9f67a80845ade7d0c")); } @Test diff --git a/src/test/java/io/api/etherscan/proxy/ProxyGasApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyGasApiTest.java similarity index 79% rename from src/test/java/io/api/etherscan/proxy/ProxyGasApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/proxy/ProxyGasApiTest.java index b4b6f37..1b40705 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyGasApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyGasApiTest.java @@ -1,7 +1,7 @@ -package io.api.etherscan.proxy; +package io.goodforgod.api.etherscan.proxy; -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidDataHexException; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidDataHexException; import java.math.BigInteger; import org.junit.jupiter.api.Test; @@ -38,6 +38,6 @@ void correctEstimatedWithData() { @Test void invalidParamWithError() { String dataCustom = "280&60106000396000f360606040526000"; - assertThrows(InvalidDataHexException.class, () -> getApi().proxy().gasEstimated(dataCustom)); + assertThrows(EtherScanInvalidDataHexException.class, () -> getApi().proxy().gasEstimated(dataCustom)); } } diff --git a/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyStorageApiTest.java similarity index 76% rename from src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/proxy/ProxyStorageApiTest.java index 6d2e8e4..2580e22 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyStorageApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyStorageApiTest.java @@ -1,7 +1,7 @@ -package io.api.etherscan.proxy; +package io.goodforgod.api.etherscan.proxy; -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidAddressException; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; import java.util.Optional; import org.junit.jupiter.api.Test; @@ -19,7 +19,7 @@ void correct() { @Test void invalidParamWithError() { - assertThrows(InvalidAddressException.class, + assertThrows(EtherScanInvalidAddressException.class, () -> getApi().proxy().storageAt("0xe03d9cce9d60f3e9f2597e13cd4c54c55330cfd", 0)); } diff --git a/src/test/java/io/api/etherscan/proxy/ProxyTxApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxApiTest.java similarity index 88% rename from src/test/java/io/api/etherscan/proxy/ProxyTxApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxApiTest.java index decf95f..d6790bd 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyTxApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxApiTest.java @@ -1,8 +1,8 @@ -package io.api.etherscan.proxy; +package io.goodforgod.api.etherscan.proxy; -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidTxHashException; -import io.api.etherscan.model.proxy.TxProxy; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidTxHashException; +import io.goodforgod.api.etherscan.model.proxy.TxProxy; import java.util.Optional; import org.junit.jupiter.api.Test; @@ -51,7 +51,7 @@ void correctByBlockNo() { @Test void invalidParamWithError() { - assertThrows(InvalidTxHashException.class, + assertThrows(EtherScanInvalidTxHashException.class, () -> getApi().proxy().tx("0xe2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1")); } diff --git a/src/test/java/io/api/etherscan/proxy/ProxyTxCountApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxCountApiTest.java similarity index 81% rename from src/test/java/io/api/etherscan/proxy/ProxyTxCountApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxCountApiTest.java index 0083f7a..a2327da 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyTxCountApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxCountApiTest.java @@ -1,7 +1,7 @@ -package io.api.etherscan.proxy; +package io.goodforgod.api.etherscan.proxy; -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidAddressException; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; import org.junit.jupiter.api.Test; /** @@ -24,7 +24,7 @@ void correctByBlockNo() { @Test void invalidParamWithError() { - assertThrows(InvalidAddressException.class, + assertThrows(EtherScanInvalidAddressException.class, () -> getApi().proxy().txSendCount("0xe03d9cce9d60f3e9f2597e13cd4c54c55330cfd")); } diff --git a/src/test/java/io/api/etherscan/proxy/ProxyTxReceiptApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxReceiptApiTest.java similarity index 85% rename from src/test/java/io/api/etherscan/proxy/ProxyTxReceiptApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxReceiptApiTest.java index 0159ed9..ba6370c 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyTxReceiptApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxReceiptApiTest.java @@ -1,8 +1,8 @@ -package io.api.etherscan.proxy; +package io.goodforgod.api.etherscan.proxy; -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidTxHashException; -import io.api.etherscan.model.proxy.ReceiptProxy; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidTxHashException; +import io.goodforgod.api.etherscan.model.proxy.ReceiptProxy; import java.util.Optional; import org.junit.jupiter.api.Test; @@ -39,7 +39,7 @@ void correct() { @Test void invalidParamWithError() { - assertThrows(InvalidTxHashException.class, () -> getApi().proxy() + assertThrows(EtherScanInvalidTxHashException.class, () -> getApi().proxy() .txReceipt("0xe2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1")); } diff --git a/src/test/java/io/api/etherscan/proxy/ProxyTxSendRawApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxSendRawApiTest.java similarity index 62% rename from src/test/java/io/api/etherscan/proxy/ProxyTxSendRawApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxSendRawApiTest.java index 676dc3a..9f69060 100644 --- a/src/test/java/io/api/etherscan/proxy/ProxyTxSendRawApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxSendRawApiTest.java @@ -1,8 +1,8 @@ -package io.api.etherscan.proxy; +package io.goodforgod.api.etherscan.proxy; -import io.api.ApiRunner; -import io.api.etherscan.error.EtherScanException; -import io.api.etherscan.error.InvalidDataHexException; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidDataHexException; +import io.goodforgod.api.etherscan.error.EtherScanResponseException; import java.util.Optional; import org.junit.jupiter.api.Test; @@ -21,12 +21,12 @@ void correct() { @Test void invalidParamWithError() { - assertThrows(InvalidDataHexException.class, () -> getApi().proxy().txSendRaw("5151=0561")); + assertThrows(EtherScanInvalidDataHexException.class, () -> getApi().proxy().txSendRaw("5151=0561")); } @Test void invalidParamEtherScanDataException() { - assertThrows(EtherScanException.class, () -> getApi().proxy().txSendRaw("0x1")); + assertThrows(EtherScanResponseException.class, () -> getApi().proxy().txSendRaw("0x1")); } void correctParamWithEmptyExpectedResult() { diff --git a/src/test/java/io/api/etherscan/statistic/StatisticPriceApiTest.java b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTest.java similarity index 81% rename from src/test/java/io/api/etherscan/statistic/StatisticPriceApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTest.java index 9f89738..eb43b6e 100644 --- a/src/test/java/io/api/etherscan/statistic/StatisticPriceApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTest.java @@ -1,7 +1,7 @@ -package io.api.etherscan.statistic; +package io.goodforgod.api.etherscan.statistic; -import io.api.ApiRunner; -import io.api.etherscan.model.Price; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.model.Price; import org.junit.jupiter.api.Test; /** diff --git a/src/test/java/io/api/etherscan/statistic/StatisticSupplyApiTest.java b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyApiTest.java similarity index 82% rename from src/test/java/io/api/etherscan/statistic/StatisticSupplyApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyApiTest.java index 32c3018..c1e8e58 100644 --- a/src/test/java/io/api/etherscan/statistic/StatisticSupplyApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyApiTest.java @@ -1,7 +1,7 @@ -package io.api.etherscan.statistic; +package io.goodforgod.api.etherscan.statistic; -import io.api.ApiRunner; -import io.api.etherscan.model.Supply; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.model.Supply; import java.math.BigInteger; import org.junit.jupiter.api.Test; diff --git a/src/test/java/io/api/etherscan/statistic/StatisticTokenSupplyApiTest.java b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticTokenSupplyApiTest.java similarity index 67% rename from src/test/java/io/api/etherscan/statistic/StatisticTokenSupplyApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/statistic/StatisticTokenSupplyApiTest.java index aefb2bd..84c086a 100644 --- a/src/test/java/io/api/etherscan/statistic/StatisticTokenSupplyApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticTokenSupplyApiTest.java @@ -1,7 +1,7 @@ -package io.api.etherscan.statistic; +package io.goodforgod.api.etherscan.statistic; -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidAddressException; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; import java.math.BigInteger; import org.junit.jupiter.api.Test; @@ -20,7 +20,8 @@ void correct() { @Test void invalidParamWithError() { - assertThrows(InvalidAddressException.class, () -> getApi().stats().supply("0x7d90b64a1a57749b0f932f1a3395792e12e7055")); + assertThrows(EtherScanInvalidAddressException.class, + () -> getApi().stats().supply("0x7d90b64a1a57749b0f932f1a3395792e12e7055")); } @Test diff --git a/src/test/java/io/api/support/AddressUtil.java b/src/test/java/io/goodforgod/api/etherscan/support/AddressUtil.java similarity index 98% rename from src/test/java/io/api/support/AddressUtil.java rename to src/test/java/io/goodforgod/api/etherscan/support/AddressUtil.java index da04c37..fa007db 100644 --- a/src/test/java/io/api/support/AddressUtil.java +++ b/src/test/java/io/goodforgod/api/etherscan/support/AddressUtil.java @@ -1,4 +1,4 @@ -package io.api.support; +package io.goodforgod.api.etherscan.support; import java.util.ArrayList; import java.util.List; diff --git a/src/test/java/io/api/etherscan/transaction/TransactionExecApiTest.java b/src/test/java/io/goodforgod/api/etherscan/transaction/TransactionExecApiTest.java similarity index 66% rename from src/test/java/io/api/etherscan/transaction/TransactionExecApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/transaction/TransactionExecApiTest.java index de67a02..a2a5860 100644 --- a/src/test/java/io/api/etherscan/transaction/TransactionExecApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/transaction/TransactionExecApiTest.java @@ -1,8 +1,8 @@ -package io.api.etherscan.transaction; +package io.goodforgod.api.etherscan.transaction; -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidTxHashException; -import io.api.etherscan.model.Status; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidTxHashException; +import io.goodforgod.api.etherscan.model.Status; import java.util.Optional; import org.junit.jupiter.api.Test; @@ -14,7 +14,7 @@ class TransactionExecApiTest extends ApiRunner { @Test void correct() { - Optional status = getApi().txs().execStatus("0x15f8e5ea1079d9a0bb04a4c58ae5fe7654b5b2b4463375ff7ffb490aa0032f3a"); + Optional status = getApi().txs().statusExec("0x15f8e5ea1079d9a0bb04a4c58ae5fe7654b5b2b4463375ff7ffb490aa0032f3a"); assertTrue(status.isPresent()); assertTrue(status.get().haveError()); assertNotNull(status.get().getErrDescription()); @@ -27,13 +27,13 @@ void correct() { @Test void invalidParamWithError() { - assertThrows(InvalidTxHashException.class, - () -> getApi().txs().execStatus("0xb513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b")); + assertThrows(EtherScanInvalidTxHashException.class, + () -> getApi().txs().statusExec("0xb513dd971aad228eb31f54489803639de167309ac72de68ecdaeb022a7ab42b")); } @Test void correctParamWithEmptyExpectedResult() { - Optional status = getApi().txs().execStatus("0x55f8e5ea1079d9a0bb04a4c58ae5fe7654b5b2b4463375ff7ffb490aa0032f3a"); + Optional status = getApi().txs().statusExec("0x55f8e5ea1079d9a0bb04a4c58ae5fe7654b5b2b4463375ff7ffb490aa0032f3a"); assertTrue(status.isPresent()); assertFalse(status.get().haveError()); } diff --git a/src/test/java/io/api/etherscan/transaction/TransactionReceiptApiTest.java b/src/test/java/io/goodforgod/api/etherscan/transaction/TransactionReceiptApiTest.java similarity index 61% rename from src/test/java/io/api/etherscan/transaction/TransactionReceiptApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/transaction/TransactionReceiptApiTest.java index 94b93b3..83ca5af 100644 --- a/src/test/java/io/api/etherscan/transaction/TransactionReceiptApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/transaction/TransactionReceiptApiTest.java @@ -1,7 +1,7 @@ -package io.api.etherscan.transaction; +package io.goodforgod.api.etherscan.transaction; -import io.api.ApiRunner; -import io.api.etherscan.error.InvalidTxHashException; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidTxHashException; import java.util.Optional; import org.junit.jupiter.api.Test; @@ -14,21 +14,21 @@ class TransactionReceiptApiTest extends ApiRunner { @Test void correct() { Optional status = getApi().txs() - .receiptStatus("0x513c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76"); + .statusReceipt("0x513c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76"); assertTrue(status.isPresent()); assertTrue(status.get()); } @Test void invalidParamWithError() { - assertThrows(InvalidTxHashException.class, - () -> getApi().txs().receiptStatus("0x13c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76")); + assertThrows(EtherScanInvalidTxHashException.class, + () -> getApi().txs().statusReceipt("0x13c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76")); } @Test void correctParamWithEmptyExpectedResult() { Optional status = getApi().txs() - .receiptStatus("0x113c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76"); + .statusReceipt("0x113c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76"); assertFalse(status.isPresent()); } } diff --git a/src/test/java/io/api/util/BasicUtilsTests.java b/src/test/java/io/goodforgod/api/etherscan/util/BasicUtilsTests.java similarity index 75% rename from src/test/java/io/api/util/BasicUtilsTests.java rename to src/test/java/io/goodforgod/api/etherscan/util/BasicUtilsTests.java index 36c22cb..90a2933 100644 --- a/src/test/java/io/api/util/BasicUtilsTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/util/BasicUtilsTests.java @@ -1,11 +1,11 @@ -package io.api.util; +package io.goodforgod.api.etherscan.util; -import static io.api.etherscan.util.BasicUtils.*; +import static io.goodforgod.api.etherscan.util.BasicUtils.*; import com.google.gson.Gson; -import io.api.ApiRunner; -import io.api.etherscan.error.EtherScanException; -import io.api.etherscan.model.utility.StringResponseTO; +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanResponseException; +import io.goodforgod.api.etherscan.model.response.StringResponseTO; import java.util.ArrayList; import java.util.List; import org.junit.jupiter.api.Test; @@ -21,7 +21,7 @@ void responseValidateEmpty() { String response = "{\"status\":\"0\",\"message\":\"No ether\",\"result\":\"status\"}"; StringResponseTO responseTO = new Gson().fromJson(response, StringResponseTO.class); - assertThrows(EtherScanException.class, () -> validateTxResponse(responseTO)); + assertThrows(EtherScanResponseException.class, () -> validateTxResponse(responseTO)); } @Test @@ -77,12 +77,12 @@ void isNotHexInvalid() { @Test void isResponseStatusInvalidThrows() { StringResponseTO responseTO = new StringResponseTO(); - assertThrows(EtherScanException.class, () -> validateTxResponse(responseTO)); + assertThrows(EtherScanResponseException.class, () -> validateTxResponse(responseTO)); } @Test void isResponseNullThrows() { StringResponseTO responseTO = null; - assertThrows(EtherScanException.class, () -> validateTxResponse(responseTO)); + assertThrows(EtherScanResponseException.class, () -> validateTxResponse(responseTO)); } } From 420c68f201128dc60f854ef60572c0bdd5877faa Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sat, 13 May 2023 20:47:09 +0300 Subject: [PATCH 14/61] [2.0.0-SNAPSHOT] GasTrackerAPI refactored to new API EthHttpClient package refactoring --- .../goodforgod/api/etherscan/AccountAPI.java | 14 +++---- .../api/etherscan/AccountAPIProvider.java | 13 ++++--- .../api/etherscan/BasicProvider.java | 8 ++-- .../api/etherscan/EtherScanAPI.java | 4 ++ .../api/etherscan/EtherScanAPIProvider.java | 23 +++++++---- .../api/etherscan/GasTrackerAPI.java | 24 ++++++++++++ .../api/etherscan/GasTrackerAPIProvider.java | 37 ++++++++++++++++++ .../api/etherscan/GasTrackerApiProvider.java | 39 ------------------- .../api/etherscan/IGasTrackerApi.java | 23 ----------- .../api/etherscan/model/BlockUncle.java | 1 - .../api/etherscan/model/GasOracle.java | 15 ++++--- .../model/response/GasOracleResponseTO.java | 6 +-- .../account/AccountTxRc721TokenTest.java | 4 +- .../SemaphoreRequestQueueManagerTest.java | 6 +-- 14 files changed, 113 insertions(+), 104 deletions(-) create mode 100644 src/main/java/io/goodforgod/api/etherscan/GasTrackerAPI.java create mode 100644 src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java delete mode 100644 src/main/java/io/goodforgod/api/etherscan/GasTrackerApiProvider.java delete mode 100644 src/main/java/io/goodforgod/api/etherscan/IGasTrackerApi.java rename src/main/java/io/{ => goodforgod}/api/etherscan/model/GasOracle.java (79%) diff --git a/src/main/java/io/goodforgod/api/etherscan/AccountAPI.java b/src/main/java/io/goodforgod/api/etherscan/AccountAPI.java index 1baefc9..40da2eb 100644 --- a/src/main/java/io/goodforgod/api/etherscan/AccountAPI.java +++ b/src/main/java/io/goodforgod/api/etherscan/AccountAPI.java @@ -112,21 +112,21 @@ public interface AccountAPI { /** * All ERC-20 token txs for given address and contract address * - * @param address get txs for + * @param address get txs for * @param contractAddress contract address to get txs for - * @param startBlock tx from this blockNumber - * @param endBlock tx to this blockNumber + * @param startBlock tx from this blockNumber + * @param endBlock tx to this blockNumber * @return txs for address - * @throws ApiException parent exception class + * @throws EtherScanException parent exception class */ @NotNull - List txsToken(String address, String contractAddress, long startBlock, long endBlock) throws ApiException; + List txsERC20(String address, String contractAddress, long startBlock, long endBlock) throws EtherScanException; @NotNull - List txsToken(String address, String contractAddress, long startBlock) throws ApiException; + List txsERC20(String address, String contractAddress, long startBlock) throws EtherScanException; @NotNull - List txsToken(String address, String contractAddress) throws ApiException; + List txsERC20(String address, String contractAddress) throws EtherScanException; /** * All ERC-721 (NFT) token txs for given address diff --git a/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java index e884399..3cc5409 100644 --- a/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java @@ -234,19 +234,20 @@ public List txsERC20(String address, long startBlock, long endBlock) th @NotNull @Override - public List txsToken(final String address, final String contractAddress) throws ApiException { - return txsToken(address, contractAddress, MIN_START_BLOCK); + public List txsERC20(String address, String contractAddress) throws EtherScanException { + return txsERC20(address, contractAddress, MIN_START_BLOCK); } @NotNull @Override - public List txsToken(final String address, final String contractAddress, final long startBlock) throws ApiException { - return txsToken(address, contractAddress, startBlock, MAX_END_BLOCK); + public List txsERC20(String address, String contractAddress, long startBlock) throws EtherScanException { + return txsERC20(address, contractAddress, startBlock, MAX_END_BLOCK); } @NotNull @Override - public List txsToken(final String address, final String contractAddress, final long startBlock, final long endBlock) throws ApiException { + public List txsERC20(String address, String contractAddress, long startBlock, long endBlock) + throws EtherScanException { BasicUtils.validateAddress(address); final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); @@ -255,7 +256,7 @@ public List txsToken(final String address, final String contractAddress final String urlParams = ACT_TX_TOKEN_ACTION + offsetParam + ADDRESS_PARAM + address + CONTRACT_PARAM + contractAddress + blockParam + SORT_ASC_PARAM; - return getRequestUsingOffset(urlParams, TxTokenResponseTO.class); + return getRequestUsingOffset(urlParams, TxERC20ResponseTO.class); } @NotNull diff --git a/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java b/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java index 4fd625a..ec5a85a 100644 --- a/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java @@ -32,14 +32,14 @@ abstract class BasicProvider { private final RequestQueueManager queue; private final Gson gson; - BasicProvider(RequestQueueManager queue, + BasicProvider(RequestQueueManager requestQueueManager, String module, String baseUrl, - EthHttpClient executor) { - this.queue = queue; + EthHttpClient ethHttpClient) { + this.queue = requestQueueManager; this.module = "&module=" + module; this.baseUrl = baseUrl; - this.executor = executor; + this.executor = ethHttpClient; this.gson = new GsonConfiguration().builder().create(); } diff --git a/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java index 76dcab7..d902074 100644 --- a/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java +++ b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java @@ -34,6 +34,10 @@ public interface EtherScanAPI extends AutoCloseable { @NotNull StatisticAPI stats(); + @NotNull + GasTrackerAPI gasTracker(); + + @NotNull static Builder builder() { return new EthScanAPIBuilder(); } diff --git a/src/main/java/io/goodforgod/api/etherscan/EtherScanAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPIProvider.java index 0043e37..675836f 100644 --- a/src/main/java/io/goodforgod/api/etherscan/EtherScanAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPIProvider.java @@ -21,23 +21,25 @@ final class EtherScanAPIProvider implements EtherScanAPI { private final ProxyAPI proxy; private final StatisticAPI stats; private final TransactionAPI txs; + private final GasTrackerAPI gasTracker; EtherScanAPIProvider(String apiKey, EthNetwork network, Supplier executorSupplier, RequestQueueManager queue) { // EtherScan 1request\5sec limit support by queue manager - final EthHttpClient executor = executorSupplier.get(); + final EthHttpClient ethHttpClient = executorSupplier.get(); final String baseUrl = network.domain() + "?apikey=" + apiKey; this.requestQueueManager = queue; - this.account = new AccountAPIProvider(queue, baseUrl, executor); - this.block = new BlockAPIProvider(queue, baseUrl, executor); - this.contract = new ContractAPIProvider(queue, baseUrl, executor); - this.logs = new LogsAPIProvider(queue, baseUrl, executor); - this.proxy = new ProxyAPIProvider(queue, baseUrl, executor); - this.stats = new StatisticAPIProvider(queue, baseUrl, executor); - this.txs = new TransactionAPIProvider(queue, baseUrl, executor); + this.account = new AccountAPIProvider(queue, baseUrl, ethHttpClient); + this.block = new BlockAPIProvider(queue, baseUrl, ethHttpClient); + this.contract = new ContractAPIProvider(queue, baseUrl, ethHttpClient); + this.logs = new LogsAPIProvider(queue, baseUrl, ethHttpClient); + this.proxy = new ProxyAPIProvider(queue, baseUrl, ethHttpClient); + this.stats = new StatisticAPIProvider(queue, baseUrl, ethHttpClient); + this.txs = new TransactionAPIProvider(queue, baseUrl, ethHttpClient); + this.gasTracker = new GasTrackerAPIProvider(queue, baseUrl, ethHttpClient); } @NotNull @@ -82,6 +84,11 @@ public StatisticAPI stats() { return stats; } + @Override + public @NotNull GasTrackerAPI gasTracker() { + return gasTracker; + } + @Override public void close() throws Exception { requestQueueManager.close(); diff --git a/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPI.java b/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPI.java new file mode 100644 index 0000000..d49e14f --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPI.java @@ -0,0 +1,24 @@ +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.model.GasOracle; +import org.jetbrains.annotations.NotNull; + +/** + * EtherScan - API Descriptions + * ... + * + * @author Abhay Gupta + * @since 14.11.2022 + */ +public interface GasTrackerAPI { + + /** + * GasOracle details + * + * @return fast, suggested gas price + * @throws EtherScanException parent exception class + */ + @NotNull + GasOracle oracle() throws EtherScanException; +} diff --git a/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java new file mode 100644 index 0000000..faa01e5 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java @@ -0,0 +1,37 @@ +package io.goodforgod.api.etherscan; + +import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.error.EtherScanResponseException; +import io.goodforgod.api.etherscan.executor.EthHttpClient; +import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import io.goodforgod.api.etherscan.model.GasOracle; +import io.goodforgod.api.etherscan.model.response.GasOracleResponseTO; +import org.jetbrains.annotations.NotNull; + +/** + * GasTracker API Implementation + * + * @see GasTrackerAPI + * @author Abhay Gupta + * @since 14.11.2022 + */ +final class GasTrackerAPIProvider extends BasicProvider implements GasTrackerAPI { + + private static final String ACT_GAS_ORACLE_PARAM = ACT_PREFIX + "gasoracle"; + + GasTrackerAPIProvider(RequestQueueManager queue, + String baseUrl, + EthHttpClient ethHttpClient) { + super(queue, "gastracker", baseUrl, ethHttpClient); + } + + @NotNull + @Override + public GasOracle oracle() throws EtherScanException { + final GasOracleResponseTO response = getRequest(ACT_GAS_ORACLE_PARAM, GasOracleResponseTO.class); + if (response.getStatus() != 1) + throw new EtherScanResponseException(response); + + return response.getResult(); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/GasTrackerApiProvider.java b/src/main/java/io/goodforgod/api/etherscan/GasTrackerApiProvider.java deleted file mode 100644 index 16d2e63..0000000 --- a/src/main/java/io/goodforgod/api/etherscan/GasTrackerApiProvider.java +++ /dev/null @@ -1,39 +0,0 @@ -package io.api.etherscan.core.impl; - -import io.api.etherscan.core.IGasTrackerApi; -import io.api.etherscan.error.ApiException; -import io.api.etherscan.error.EtherScanException; -import io.api.etherscan.executor.IHttpExecutor; -import io.api.etherscan.manager.IQueueManager; -import io.api.etherscan.model.GasOracle; -import io.api.etherscan.model.utility.GasOracleResponseTO; -import org.jetbrains.annotations.NotNull; - -/** - * GasTracker API Implementation - * - * @see IGasTrackerApi - * - * @author Abhay Gupta - * @since 14.11.2022 - */ -public class GasTrackerApiProvider extends BasicProvider implements IGasTrackerApi { - - private static final String ACT_GAS_ORACLE_PARAM = ACT_PREFIX + "gasoracle"; - - GasTrackerApiProvider(final IQueueManager queue, - final String baseUrl, - final IHttpExecutor executor) { - super(queue, "gastracker", baseUrl, executor); - } - - @NotNull - @Override - public GasOracle gasoracle() throws ApiException { - final GasOracleResponseTO response = getRequest(ACT_GAS_ORACLE_PARAM, GasOracleResponseTO.class); - if (response.getStatus() != 1) - throw new EtherScanException(response); - - return response.getResult(); - } -} diff --git a/src/main/java/io/goodforgod/api/etherscan/IGasTrackerApi.java b/src/main/java/io/goodforgod/api/etherscan/IGasTrackerApi.java deleted file mode 100644 index 894713f..0000000 --- a/src/main/java/io/goodforgod/api/etherscan/IGasTrackerApi.java +++ /dev/null @@ -1,23 +0,0 @@ -package io.api.etherscan.core; - -import io.api.etherscan.error.ApiException; -import io.api.etherscan.model.GasOracle; -import org.jetbrains.annotations.NotNull; - -/** - * EtherScan - API Descriptions https://docs.etherscan.io/api-endpoints/gas-tracker - * - * @author Abhay Gupta - * @since 14.11.2022 - */ -public interface IGasTrackerApi { - - /** - * GasOracle details - * - * @return fast, suggested gas price - * @throws ApiException parent exception class - */ - @NotNull - GasOracle gasoracle() throws ApiException; -} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java b/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java index 34f8f30..add3bd1 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java @@ -1,7 +1,6 @@ package io.goodforgod.api.etherscan.model; import io.goodforgod.api.etherscan.util.BasicUtils; - import java.math.BigInteger; import java.util.List; diff --git a/src/main/java/io/api/etherscan/model/GasOracle.java b/src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java similarity index 79% rename from src/main/java/io/api/etherscan/model/GasOracle.java rename to src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java index f3f66c2..0ea1715 100644 --- a/src/main/java/io/api/etherscan/model/GasOracle.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java @@ -1,15 +1,14 @@ -package io.api.etherscan.model; +package io.goodforgod.api.etherscan.model; import java.math.BigInteger; import java.util.Objects; /** - * ! NO DESCRIPTION ! - * * @author Abhay Gupta * @since 14.11.2022 */ public class GasOracle { + private Long LastBlock; private Integer SafeGasPrice; private Integer ProposeGasPrice; @@ -43,10 +42,14 @@ public String getGasUsedRatio() { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; GasOracle gasOracle = (GasOracle) o; - return LastBlock.equals(gasOracle.LastBlock) && SafeGasPrice.equals(gasOracle.SafeGasPrice) && ProposeGasPrice.equals(gasOracle.ProposeGasPrice) && FastGasPrice.equals(gasOracle.FastGasPrice) && suggestBaseFee.equals(gasOracle.suggestBaseFee) && gasUsedRatio.equals(gasOracle.gasUsedRatio); + return LastBlock.equals(gasOracle.LastBlock) && SafeGasPrice.equals(gasOracle.SafeGasPrice) + && ProposeGasPrice.equals(gasOracle.ProposeGasPrice) && FastGasPrice.equals(gasOracle.FastGasPrice) + && suggestBaseFee.equals(gasOracle.suggestBaseFee) && gasUsedRatio.equals(gasOracle.gasUsedRatio); } @Override diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/GasOracleResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/GasOracleResponseTO.java index f0c1fd5..751854c 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/response/GasOracleResponseTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/GasOracleResponseTO.java @@ -1,10 +1,8 @@ -package io.api.etherscan.model.utility; +package io.goodforgod.api.etherscan.model.response; -import io.api.etherscan.model.GasOracle; +import io.goodforgod.api.etherscan.model.GasOracle; /** - * ! NO DESCRIPTION ! - * * @author Abhay Gupta * @since 14.11.2022 */ diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTest.java index c85aaa4..31c8533 100644 --- a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTest.java @@ -2,10 +2,8 @@ import io.goodforgod.api.etherscan.ApiRunner; import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; -import io.goodforgod.api.etherscan.model.TxERC20; -import java.util.List; - import io.goodforgod.api.etherscan.model.TxERC721; +import java.util.List; import org.junit.jupiter.api.Test; /** diff --git a/src/test/java/io/goodforgod/api/etherscan/manager/SemaphoreRequestQueueManagerTest.java b/src/test/java/io/goodforgod/api/etherscan/manager/SemaphoreRequestQueueManagerTest.java index 23d14a2..0d6daf6 100644 --- a/src/test/java/io/goodforgod/api/etherscan/manager/SemaphoreRequestQueueManagerTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/manager/SemaphoreRequestQueueManagerTest.java @@ -3,11 +3,10 @@ import io.goodforgod.api.etherscan.ApiRunner; import io.goodforgod.api.etherscan.manager.impl.FakeRequestQueueManager; import io.goodforgod.api.etherscan.manager.impl.SemaphoreRequestQueueManager; +import java.time.Duration; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Timeout; -import java.time.Duration; - /** * @author GoodforGod * @since 03.11.2018 @@ -38,7 +37,8 @@ void queueManager() { @Test @Timeout(4500) void queueManagerWithDelay() { - RequestQueueManager requestQueueManager = new SemaphoreRequestQueueManager(1, Duration.ofSeconds(2), Duration.ofSeconds(2)); + RequestQueueManager requestQueueManager = new SemaphoreRequestQueueManager(1, Duration.ofSeconds(2), + Duration.ofSeconds(2)); requestQueueManager.takeTurn(); requestQueueManager.takeTurn(); assertNotNull(requestQueueManager); From b9a8dda6d503b953d7e9aa7257a0d4e9bac2d351 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 14 May 2023 00:50:20 +0300 Subject: [PATCH 15/61] [2.0.0-SNAPSHOT] Models builders added --- .../api/etherscan/AccountAPIProvider.java | 2 +- .../api/etherscan/BasicProvider.java | 2 +- .../impl/SemaphoreRequestQueueManager.java | 5 +- .../goodforgod/api/etherscan/model/Abi.java | 30 +++- .../api/etherscan/model/Balance.java | 8 +- .../api/etherscan/model/BaseTx.java | 22 +-- .../goodforgod/api/etherscan/model/Block.java | 49 +++++- .../api/etherscan/model/BlockUncle.java | 98 +++++++++++ .../api/etherscan/model/GasOracle.java | 57 +++++++ .../goodforgod/api/etherscan/model/Log.java | 93 +++++++++- .../goodforgod/api/etherscan/model/Price.java | 45 +++++ .../api/etherscan/model/Status.java | 29 ++++ .../io/goodforgod/api/etherscan/model/Tx.java | 144 ++++++++++++++++ .../api/etherscan/model/TxERC20.java | 153 +++++++++++++++++ .../api/etherscan/model/TxERC721.java | 153 +++++++++++++++++ .../api/etherscan/model/TxInternal.java | 117 +++++++++++++ .../goodforgod/api/etherscan/model/Wei.java | 2 +- .../api/etherscan/model/proxy/BlockProxy.java | 160 ++++++++++++++++++ .../etherscan/model/proxy/ReceiptProxy.java | 103 +++++++++++ .../api/etherscan/model/proxy/TxProxy.java | 118 +++++++++++++ 20 files changed, 1361 insertions(+), 29 deletions(-) diff --git a/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java index 3cc5409..9160bb4 100644 --- a/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java @@ -99,7 +99,7 @@ public List balances(List addresses) throws EtherScanException if (!BasicUtils.isEmpty(response.getResult())) balances.addAll(response.getResult().stream() - .map(Balance::of) + .map(r -> new Balance(r.getAccount(), new BigInteger(r.getBalance()))) .collect(Collectors.toList())); } diff --git a/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java b/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java index ec5a85a..a4ce2a6 100644 --- a/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java @@ -1,6 +1,6 @@ package io.goodforgod.api.etherscan; -import com.google.gson.*; +import com.google.gson.Gson; import io.goodforgod.api.etherscan.error.EtherScanException; import io.goodforgod.api.etherscan.error.EtherScanParseException; import io.goodforgod.api.etherscan.error.EtherScanRateLimitException; diff --git a/src/main/java/io/goodforgod/api/etherscan/manager/impl/SemaphoreRequestQueueManager.java b/src/main/java/io/goodforgod/api/etherscan/manager/impl/SemaphoreRequestQueueManager.java index cfd745f..ff12cd1 100644 --- a/src/main/java/io/goodforgod/api/etherscan/manager/impl/SemaphoreRequestQueueManager.java +++ b/src/main/java/io/goodforgod/api/etherscan/manager/impl/SemaphoreRequestQueueManager.java @@ -2,7 +2,10 @@ import io.goodforgod.api.etherscan.manager.RequestQueueManager; import java.time.Duration; -import java.util.concurrent.*; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; /** * Queue Semaphore implementation with size and reset time as params diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Abi.java b/src/main/java/io/goodforgod/api/etherscan/model/Abi.java index b575c56..3fce40a 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Abi.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Abi.java @@ -8,8 +8,8 @@ */ public class Abi { - private String contractAbi; - private boolean isVerified; + private final String contractAbi; + private final boolean isVerified; private Abi(String contractAbi, boolean isVerified) { this.contractAbi = contractAbi; @@ -70,4 +70,30 @@ public String toString() { ", isVerified=" + isVerified + '}'; } + + public static AbiBuilder builder() { + return new AbiBuilder(); + } + + public static final class AbiBuilder { + + private String contractAbi; + private boolean isVerified; + + private AbiBuilder() {} + + public AbiBuilder withContractAbi(String contractAbi) { + this.contractAbi = contractAbi; + return this; + } + + public AbiBuilder withIsVerified(boolean isVerified) { + this.isVerified = isVerified; + return this; + } + + public Abi build() { + return new Abi(contractAbi, isVerified); + } + } } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Balance.java b/src/main/java/io/goodforgod/api/etherscan/model/Balance.java index d147a2c..38379e6 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Balance.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Balance.java @@ -1,6 +1,5 @@ package io.goodforgod.api.etherscan.model; -import io.goodforgod.api.etherscan.model.response.BalanceTO; import java.math.BigInteger; import java.util.Objects; @@ -14,16 +13,11 @@ public class Balance { private final Wei balance; private final String address; - public Balance(final String address, - final BigInteger balance) { + public Balance(String address, BigInteger balance) { this.address = address; this.balance = new Wei(balance); } - public static Balance of(BalanceTO balance) { - return new Balance(balance.getAccount(), new BigInteger(balance.getBalance())); - } - // public String getAddress() { return address; diff --git a/src/main/java/io/goodforgod/api/etherscan/model/BaseTx.java b/src/main/java/io/goodforgod/api/etherscan/model/BaseTx.java index 1fb53e1..e159d3b 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/BaseTx.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/BaseTx.java @@ -13,18 +13,18 @@ */ abstract class BaseTx { - private long blockNumber; - private String timeStamp; + long blockNumber; + String timeStamp; @Expose(deserialize = false, serialize = false) - private LocalDateTime _timeStamp; - private String hash; - private String from; - private String to; - private BigInteger value; - private String contractAddress; - private String input; - private BigInteger gas; - private BigInteger gasUsed; + LocalDateTime _timeStamp; + String hash; + String from; + String to; + BigInteger value; + String contractAddress; + String input; + BigInteger gas; + BigInteger gasUsed; // public long getBlockNumber() { diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Block.java b/src/main/java/io/goodforgod/api/etherscan/model/Block.java index 8b652bb..129ca39 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Block.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Block.java @@ -12,11 +12,11 @@ */ public class Block { - private long blockNumber; - private BigInteger blockReward; - private String timeStamp; + long blockNumber; + BigInteger blockReward; + String timeStamp; @Expose(deserialize = false, serialize = false) - private LocalDateTime _timeStamp; + LocalDateTime _timeStamp; // public long getBlockNumber() { @@ -60,4 +60,45 @@ public String toString() { ", _timeStamp=" + _timeStamp + '}'; } + + public static BlockBuilder builder() { + return new BlockBuilder(); + } + + public static class BlockBuilder { + + private long blockNumber; + private BigInteger blockReward; + private LocalDateTime timeStamp; + + BlockBuilder() {} + + public static BlockBuilder aBlock() { + return new BlockBuilder(); + } + + public BlockBuilder withBlockNumber(long blockNumber) { + this.blockNumber = blockNumber; + return this; + } + + public BlockBuilder withBlockReward(BigInteger blockReward) { + this.blockReward = blockReward; + return this; + } + + public BlockBuilder withTimeStamp(LocalDateTime timeStamp) { + this.timeStamp = timeStamp; + return this; + } + + public Block build() { + Block block = new Block(); + block.blockNumber = this.blockNumber; + block.blockReward = this.blockReward; + block._timeStamp = this.timeStamp; + block.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + return block; + } + } } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java b/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java index add3bd1..5cf1a3e 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java @@ -2,6 +2,8 @@ import io.goodforgod.api.etherscan.util.BasicUtils; import java.math.BigInteger; +import java.time.LocalDateTime; +import java.time.ZoneOffset; import java.util.List; /** @@ -69,6 +71,42 @@ public String toString() { ", unclePosition=" + unclePosition + '}'; } + + public static UncleBuilder builder() { + return new UncleBuilder(); + } + + public static final class UncleBuilder { + + private String miner; + private BigInteger blockreward; + private int unclePosition; + + private UncleBuilder() {} + + public UncleBuilder withMiner(String miner) { + this.miner = miner; + return this; + } + + public UncleBuilder withBlockreward(BigInteger blockreward) { + this.blockreward = blockreward; + return this; + } + + public UncleBuilder withUnclePosition(int unclePosition) { + this.unclePosition = unclePosition; + return this; + } + + public Uncle build() { + Uncle uncle = new Uncle(); + uncle.miner = this.miner; + uncle.blockreward = this.blockreward; + uncle.unclePosition = this.unclePosition; + return uncle; + } + } } private String blockMiner; @@ -124,4 +162,64 @@ public String toString() { ", uncleInclusionReward='" + uncleInclusionReward + '\'' + '}'; } + + public static BlockUncleBuilder builder() { + return new BlockUncleBuilder(); + } + + public static final class BlockUncleBuilder extends Block.BlockBuilder { + + private long blockNumber; + private BigInteger blockReward; + private LocalDateTime timeStamp; + private String blockMiner; + private List uncles; + private String uncleInclusionReward; + + private BlockUncleBuilder() { + super(); + } + + public BlockUncleBuilder withBlockNumber(long blockNumber) { + this.blockNumber = blockNumber; + return this; + } + + public BlockUncleBuilder withBlockReward(BigInteger blockReward) { + this.blockReward = blockReward; + return this; + } + + public BlockUncleBuilder withTimeStamp(LocalDateTime timeStamp) { + this.timeStamp = timeStamp; + return this; + } + + public BlockUncleBuilder withBlockMiner(String blockMiner) { + this.blockMiner = blockMiner; + return this; + } + + public BlockUncleBuilder withUncles(List uncles) { + this.uncles = uncles; + return this; + } + + public BlockUncleBuilder withUncleInclusionReward(String uncleInclusionReward) { + this.uncleInclusionReward = uncleInclusionReward; + return this; + } + + public BlockUncle build() { + BlockUncle blockUncle = new BlockUncle(); + blockUncle.uncles = this.uncles; + blockUncle.uncleInclusionReward = this.uncleInclusionReward; + blockUncle.blockNumber = this.blockNumber; + blockUncle.blockReward = this.blockReward; + blockUncle.blockMiner = this.blockMiner; + blockUncle._timeStamp = this.timeStamp; + blockUncle.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + return blockUncle; + } + } } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java b/src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java index 0ea1715..69fa6b5 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java @@ -68,4 +68,61 @@ public String toString() { ", gasUsedRatio='" + gasUsedRatio + '\'' + '}'; } + + public static GasOracleBuilder builder() { + return new GasOracleBuilder(); + } + + public static final class GasOracleBuilder { + + private Long LastBlock; + private Integer SafeGasPrice; + private Integer ProposeGasPrice; + private Integer FastGasPrice; + private Double suggestBaseFee; + private String gasUsedRatio; + + private GasOracleBuilder() {} + + public GasOracleBuilder withLastBlock(Long LastBlock) { + this.LastBlock = LastBlock; + return this; + } + + public GasOracleBuilder withSafeGasPrice(Integer SafeGasPrice) { + this.SafeGasPrice = SafeGasPrice; + return this; + } + + public GasOracleBuilder withProposeGasPrice(Integer ProposeGasPrice) { + this.ProposeGasPrice = ProposeGasPrice; + return this; + } + + public GasOracleBuilder withFastGasPrice(Integer FastGasPrice) { + this.FastGasPrice = FastGasPrice; + return this; + } + + public GasOracleBuilder withSuggestBaseFee(Double suggestBaseFee) { + this.suggestBaseFee = suggestBaseFee; + return this; + } + + public GasOracleBuilder withGasUsedRatio(String gasUsedRatio) { + this.gasUsedRatio = gasUsedRatio; + return this; + } + + public GasOracle build() { + GasOracle gasOracle = new GasOracle(); + gasOracle.ProposeGasPrice = this.ProposeGasPrice; + gasOracle.LastBlock = this.LastBlock; + gasOracle.suggestBaseFee = this.suggestBaseFee; + gasOracle.SafeGasPrice = this.SafeGasPrice; + gasOracle.FastGasPrice = this.FastGasPrice; + gasOracle.gasUsedRatio = this.gasUsedRatio; + return gasOracle; + } + } } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Log.java b/src/main/java/io/goodforgod/api/etherscan/model/Log.java index cc22b66..bd03103 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Log.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Log.java @@ -74,7 +74,7 @@ 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 milliseconds between Unix epoch and `timeStamp`. If field is empty or null, returns null */ public Long getTimeStampAsMillis() { @@ -180,4 +180,95 @@ public String toString() { ", _logIndex=" + _logIndex + '}'; } + + public static LogBuilder builder() { + return new LogBuilder(); + } + + public static final class LogBuilder { + + private Long blockNumber; + private String address; + private String transactionHash; + private Long transactionIndex; + private LocalDateTime timeStamp; + private String data; + private BigInteger gasPrice; + private BigInteger gasUsed; + private List topics; + private Long logIndex; + + private LogBuilder() {} + + public LogBuilder withBlockNumber(Long blockNumber) { + this.blockNumber = blockNumber; + return this; + } + + public LogBuilder withAddress(String address) { + this.address = address; + return this; + } + + public LogBuilder withTransactionHash(String transactionHash) { + this.transactionHash = transactionHash; + return this; + } + + public LogBuilder withTransactionIndex(Long transactionIndex) { + this.transactionIndex = transactionIndex; + return this; + } + + public LogBuilder withTimeStamp(LocalDateTime timeStamp) { + this.timeStamp = timeStamp; + return this; + } + + public LogBuilder withData(String data) { + this.data = data; + return this; + } + + public LogBuilder withGasPrice(BigInteger gasPrice) { + this.gasPrice = gasPrice; + return this; + } + + public LogBuilder withGasUsed(BigInteger gasUsed) { + this.gasUsed = gasUsed; + return this; + } + + public LogBuilder withTopics(List topics) { + this.topics = topics; + return this; + } + + public LogBuilder withLogIndex(Long logIndex) { + this.logIndex = logIndex; + return this; + } + + public Log build() { + Log log = new Log(); + log.address = this.address; + log.gasPrice = String.valueOf(this.gasPrice); + log._gasPrice = this.gasPrice; + log._logIndex = this.logIndex; + log._transactionIndex = this.transactionIndex; + log._gasUsed = this.gasUsed; + log.blockNumber = String.valueOf(this.blockNumber); + log.transactionIndex = String.valueOf(this.transactionIndex); + log.timeStamp = String.valueOf(this.timeStamp); + log.data = this.data; + log.gasUsed = String.valueOf(this.gasUsed); + log._timeStamp = this.timeStamp; + log.logIndex = String.valueOf(this.logIndex); + log._blockNumber = this.blockNumber; + log.topics = this.topics; + log.transactionHash = this.transactionHash; + return log; + } + } } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Price.java b/src/main/java/io/goodforgod/api/etherscan/model/Price.java index 712dbd8..9c72792 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Price.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Price.java @@ -87,4 +87,49 @@ public String toString() { ", ethbtc_timestamp='" + ethbtc_timestamp + '\'' + '}'; } + + public static PriceBuilder builder() { + return new PriceBuilder(); + } + + public static final class PriceBuilder { + + private double ethusd; + private double ethbtc; + private LocalDateTime ethusdTimestamp; + private LocalDateTime ethbtcTimestamp; + + private PriceBuilder() {} + + public PriceBuilder withEthusd(double ethusd) { + this.ethusd = ethusd; + return this; + } + + public PriceBuilder withEthbtc(double ethbtc) { + this.ethbtc = ethbtc; + return this; + } + + public PriceBuilder withEthusdTimestamp(LocalDateTime ethusdTimestamp) { + this.ethusdTimestamp = ethusdTimestamp; + return this; + } + + public PriceBuilder withEthbtcTimestamp(LocalDateTime ethbtcTimestamp) { + this.ethbtcTimestamp = ethbtcTimestamp; + return this; + } + + public Price build() { + Price price = new Price(); + price.ethbtc = this.ethbtc; + price.ethbtc_timestamp = String.valueOf(this.ethbtcTimestamp.toEpochSecond(ZoneOffset.UTC)); + price._ethbtc_timestamp = this.ethbtcTimestamp; + price.ethusd = this.ethusd; + price.ethusd_timestamp = String.valueOf(this.ethusdTimestamp.toEpochSecond(ZoneOffset.UTC)); + price._ethusd_timestamp = this.ethusdTimestamp; + return price; + } + } } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Status.java b/src/main/java/io/goodforgod/api/etherscan/model/Status.java index 274080a..8cdc704 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Status.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Status.java @@ -54,4 +54,33 @@ public String toString() { ", errDescription='" + errDescription + '\'' + '}'; } + + public static StatusBuilder builder() { + return new StatusBuilder(); + } + + public static final class StatusBuilder { + + private int isError; + private String errDescription; + + private StatusBuilder() {} + + public StatusBuilder withIsError(int isError) { + this.isError = isError; + return this; + } + + public StatusBuilder withErrDescription(String errDescription) { + this.errDescription = errDescription; + return this; + } + + public Status build() { + Status status = new Status(); + status.isError = this.isError; + status.errDescription = this.errDescription; + return status; + } + } } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Tx.java b/src/main/java/io/goodforgod/api/etherscan/model/Tx.java index 3ab0923..cc9be41 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Tx.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Tx.java @@ -2,6 +2,8 @@ import io.goodforgod.api.etherscan.util.BasicUtils; import java.math.BigInteger; +import java.time.LocalDateTime; +import java.time.ZoneOffset; import java.util.Objects; /** @@ -100,4 +102,146 @@ public String toString() { ", txreceipt_status='" + txreceipt_status + '\'' + "} " + super.toString(); } + + public static TxBuilder builder() { + return new TxBuilder(); + } + + public static final class TxBuilder { + + private long blockNumber; + private LocalDateTime timeStamp; + private String hash; + private String from; + private String to; + private BigInteger value; + private String contractAddress; + private String input; + private BigInteger gas; + private BigInteger gasUsed; + private long nonce; + private String blockHash; + private int transactionIndex; + private BigInteger gasPrice; + private BigInteger cumulativeGasUsed; + private long confirmations; + private String isError; + private String txreceiptStatus; + + private TxBuilder() {} + + public TxBuilder withBlockNumber(long blockNumber) { + this.blockNumber = blockNumber; + return this; + } + + public TxBuilder withTimeStamp(LocalDateTime timeStamp) { + this.timeStamp = timeStamp; + return this; + } + + public TxBuilder withHash(String hash) { + this.hash = hash; + return this; + } + + public TxBuilder withFrom(String from) { + this.from = from; + return this; + } + + public TxBuilder withTo(String to) { + this.to = to; + return this; + } + + public TxBuilder withValue(BigInteger value) { + this.value = value; + return this; + } + + public TxBuilder withContractAddress(String contractAddress) { + this.contractAddress = contractAddress; + return this; + } + + public TxBuilder withInput(String input) { + this.input = input; + return this; + } + + public TxBuilder withGas(BigInteger gas) { + this.gas = gas; + return this; + } + + public TxBuilder withGasUsed(BigInteger gasUsed) { + this.gasUsed = gasUsed; + return this; + } + + public TxBuilder withNonce(long nonce) { + this.nonce = nonce; + return this; + } + + public TxBuilder withBlockHash(String blockHash) { + this.blockHash = blockHash; + return this; + } + + public TxBuilder withTransactionIndex(int transactionIndex) { + this.transactionIndex = transactionIndex; + return this; + } + + public TxBuilder withGasPrice(BigInteger gasPrice) { + this.gasPrice = gasPrice; + return this; + } + + public TxBuilder withCumulativeGasUsed(BigInteger cumulativeGasUsed) { + this.cumulativeGasUsed = cumulativeGasUsed; + return this; + } + + public TxBuilder withConfirmations(long confirmations) { + this.confirmations = confirmations; + return this; + } + + public TxBuilder withIsError(String isError) { + this.isError = isError; + return this; + } + + public TxBuilder withTxreceiptStatus(String txreceiptStatus) { + this.txreceiptStatus = txreceiptStatus; + return this; + } + + public Tx build() { + Tx tx = new Tx(); + tx.gas = this.gas; + tx.isError = this.isError; + tx.blockHash = this.blockHash; + tx.hash = this.hash; + tx.gasUsed = this.gasUsed; + tx.from = this.from; + tx.txreceipt_status = this.txreceiptStatus; + tx.contractAddress = this.contractAddress; + tx.value = this.value; + tx.transactionIndex = this.transactionIndex; + tx.confirmations = this.confirmations; + tx.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + tx.nonce = this.nonce; + tx.blockNumber = this.blockNumber; + tx._timeStamp = this.timeStamp; + tx.to = this.to; + tx.input = this.input; + tx.cumulativeGasUsed = this.cumulativeGasUsed; + tx.gasPrice = this.gasPrice; + return tx; + } + } } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxERC20.java b/src/main/java/io/goodforgod/api/etherscan/model/TxERC20.java index 9f65d98..42ffebe 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TxERC20.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxERC20.java @@ -1,5 +1,9 @@ package io.goodforgod.api.etherscan.model; +import java.math.BigInteger; +import java.time.LocalDateTime; +import java.time.ZoneOffset; + /** * @author GoodforGod * @since 28.10.2018 @@ -68,4 +72,153 @@ public String toString() { ", confirmations=" + confirmations + "} " + super.toString(); } + + public static TxERC20Builder builder() { + return new TxERC20Builder(); + } + + public static final class TxERC20Builder { + + private long blockNumber; + private LocalDateTime timeStamp; + private String hash; + private String from; + private String to; + private BigInteger value; + private String contractAddress; + private String input; + private BigInteger gas; + private BigInteger gasUsed; + private long nonce; + private String blockHash; + private String tokenName; + private String tokenSymbol; + private String tokenDecimal; + private int transactionIndex; + private long gasPrice; + private long cumulativeGasUsed; + private long confirmations; + + private TxERC20Builder() {} + + public TxERC20Builder withBlockNumber(long blockNumber) { + this.blockNumber = blockNumber; + return this; + } + + public TxERC20Builder withTimeStamp(LocalDateTime timeStamp) { + this.timeStamp = timeStamp; + return this; + } + + public TxERC20Builder withHash(String hash) { + this.hash = hash; + return this; + } + + public TxERC20Builder withFrom(String from) { + this.from = from; + return this; + } + + public TxERC20Builder withTo(String to) { + this.to = to; + return this; + } + + public TxERC20Builder withValue(BigInteger value) { + this.value = value; + return this; + } + + public TxERC20Builder withContractAddress(String contractAddress) { + this.contractAddress = contractAddress; + return this; + } + + public TxERC20Builder withInput(String input) { + this.input = input; + return this; + } + + public TxERC20Builder withGas(BigInteger gas) { + this.gas = gas; + return this; + } + + public TxERC20Builder withGasUsed(BigInteger gasUsed) { + this.gasUsed = gasUsed; + return this; + } + + public TxERC20Builder withNonce(long nonce) { + this.nonce = nonce; + return this; + } + + public TxERC20Builder withBlockHash(String blockHash) { + this.blockHash = blockHash; + return this; + } + + public TxERC20Builder withTokenName(String tokenName) { + this.tokenName = tokenName; + return this; + } + + public TxERC20Builder withTokenSymbol(String tokenSymbol) { + this.tokenSymbol = tokenSymbol; + return this; + } + + public TxERC20Builder withTokenDecimal(String tokenDecimal) { + this.tokenDecimal = tokenDecimal; + return this; + } + + public TxERC20Builder withTransactionIndex(int transactionIndex) { + this.transactionIndex = transactionIndex; + return this; + } + + public TxERC20Builder withGasPrice(long gasPrice) { + this.gasPrice = gasPrice; + return this; + } + + public TxERC20Builder withCumulativeGasUsed(long cumulativeGasUsed) { + this.cumulativeGasUsed = cumulativeGasUsed; + return this; + } + + public TxERC20Builder withConfirmations(long confirmations) { + this.confirmations = confirmations; + return this; + } + + public TxERC20 build() { + TxERC20 txERC20 = new TxERC20(); + txERC20.gas = this.gas; + txERC20.tokenName = this.tokenName; + txERC20.hash = this.hash; + txERC20.gasUsed = this.gasUsed; + txERC20.cumulativeGasUsed = this.cumulativeGasUsed; + txERC20.from = this.from; + txERC20.tokenSymbol = this.tokenSymbol; + txERC20.transactionIndex = this.transactionIndex; + txERC20.contractAddress = this.contractAddress; + txERC20.nonce = this.nonce; + txERC20.confirmations = this.confirmations; + txERC20.value = this.value; + txERC20.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + txERC20.blockHash = this.blockHash; + txERC20.blockNumber = this.blockNumber; + txERC20._timeStamp = this.timeStamp; + txERC20.gasPrice = this.gasPrice; + txERC20.to = this.to; + txERC20.input = this.input; + txERC20.tokenDecimal = this.tokenDecimal; + return txERC20; + } + } } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxERC721.java b/src/main/java/io/goodforgod/api/etherscan/model/TxERC721.java index 5b30314..14777fc 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TxERC721.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxERC721.java @@ -1,5 +1,9 @@ package io.goodforgod.api.etherscan.model; +import java.math.BigInteger; +import java.time.LocalDateTime; +import java.time.ZoneOffset; + /** * @author GoodforGod * @since 28.10.2018 @@ -68,4 +72,153 @@ public String toString() { ", confirmations=" + confirmations + "} " + super.toString(); } + + public static TxERC721Builder builder() { + return new TxERC721Builder(); + } + + public static final class TxERC721Builder { + + private long blockNumber; + private LocalDateTime timeStamp; + private String hash; + private String from; + private String to; + private BigInteger value; + private String contractAddress; + private String input; + private BigInteger gas; + private BigInteger gasUsed; + private long nonce; + private String blockHash; + private String tokenName; + private String tokenSymbol; + private String tokenDecimal; + private int transactionIndex; + private long gasPrice; + private long cumulativeGasUsed; + private long confirmations; + + private TxERC721Builder() {} + + public TxERC721Builder withBlockNumber(long blockNumber) { + this.blockNumber = blockNumber; + return this; + } + + public TxERC721Builder withTimeStamp(LocalDateTime timeStamp) { + this.timeStamp = timeStamp; + return this; + } + + public TxERC721Builder withHash(String hash) { + this.hash = hash; + return this; + } + + public TxERC721Builder withFrom(String from) { + this.from = from; + return this; + } + + public TxERC721Builder withTo(String to) { + this.to = to; + return this; + } + + public TxERC721Builder withValue(BigInteger value) { + this.value = value; + return this; + } + + public TxERC721Builder withContractAddress(String contractAddress) { + this.contractAddress = contractAddress; + return this; + } + + public TxERC721Builder withInput(String input) { + this.input = input; + return this; + } + + public TxERC721Builder withGas(BigInteger gas) { + this.gas = gas; + return this; + } + + public TxERC721Builder withGasUsed(BigInteger gasUsed) { + this.gasUsed = gasUsed; + return this; + } + + public TxERC721Builder withNonce(long nonce) { + this.nonce = nonce; + return this; + } + + public TxERC721Builder withBlockHash(String blockHash) { + this.blockHash = blockHash; + return this; + } + + public TxERC721Builder withTokenName(String tokenName) { + this.tokenName = tokenName; + return this; + } + + public TxERC721Builder withTokenSymbol(String tokenSymbol) { + this.tokenSymbol = tokenSymbol; + return this; + } + + public TxERC721Builder withTokenDecimal(String tokenDecimal) { + this.tokenDecimal = tokenDecimal; + return this; + } + + public TxERC721Builder withTransactionIndex(int transactionIndex) { + this.transactionIndex = transactionIndex; + return this; + } + + public TxERC721Builder withGasPrice(long gasPrice) { + this.gasPrice = gasPrice; + return this; + } + + public TxERC721Builder withCumulativeGasUsed(long cumulativeGasUsed) { + this.cumulativeGasUsed = cumulativeGasUsed; + return this; + } + + public TxERC721Builder withConfirmations(long confirmations) { + this.confirmations = confirmations; + return this; + } + + public TxERC721 build() { + TxERC721 txERC721 = new TxERC721(); + txERC721.gas = this.gas; + txERC721.tokenName = this.tokenName; + txERC721.hash = this.hash; + txERC721.gasUsed = this.gasUsed; + txERC721.nonce = this.nonce; + txERC721.from = this.from; + txERC721.gasPrice = this.gasPrice; + txERC721.contractAddress = this.contractAddress; + txERC721.cumulativeGasUsed = this.cumulativeGasUsed; + txERC721.value = this.value; + txERC721.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + txERC721.blockNumber = this.blockNumber; + txERC721._timeStamp = this.timeStamp; + txERC721.tokenDecimal = this.tokenDecimal; + txERC721.transactionIndex = this.transactionIndex; + txERC721.to = this.to; + txERC721.confirmations = this.confirmations; + txERC721.input = this.input; + txERC721.blockHash = this.blockHash; + txERC721.tokenSymbol = this.tokenSymbol; + return txERC721; + } + } } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java b/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java index 244e0b7..f1d1edf 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java @@ -1,5 +1,8 @@ package io.goodforgod.api.etherscan.model; +import java.math.BigInteger; +import java.time.LocalDateTime; +import java.time.ZoneOffset; import java.util.Objects; /** @@ -74,4 +77,118 @@ public String toString() { ", errCode='" + errCode + '\'' + "} " + super.toString(); } + + public static TxInternalBuilder builder() { + return new TxInternalBuilder(); + } + + public static final class TxInternalBuilder { + + private long blockNumber; + private LocalDateTime timeStamp; + private String hash; + private String from; + private String to; + private BigInteger value; + private String contractAddress; + private String input; + private BigInteger gas; + private BigInteger gasUsed; + private String type; + private String traceId; + private int isError; + private String errCode; + + private TxInternalBuilder() {} + + public TxInternalBuilder withBlockNumber(long blockNumber) { + this.blockNumber = blockNumber; + return this; + } + + public TxInternalBuilder with_timeStamp(LocalDateTime timeStamp) { + this.timeStamp = timeStamp; + return this; + } + + public TxInternalBuilder withHash(String hash) { + this.hash = hash; + return this; + } + + public TxInternalBuilder withFrom(String from) { + this.from = from; + return this; + } + + public TxInternalBuilder withTo(String to) { + this.to = to; + return this; + } + + public TxInternalBuilder withValue(BigInteger value) { + this.value = value; + return this; + } + + public TxInternalBuilder withContractAddress(String contractAddress) { + this.contractAddress = contractAddress; + return this; + } + + public TxInternalBuilder withInput(String input) { + this.input = input; + return this; + } + + public TxInternalBuilder withGas(BigInteger gas) { + this.gas = gas; + return this; + } + + public TxInternalBuilder withGasUsed(BigInteger gasUsed) { + this.gasUsed = gasUsed; + return this; + } + + public TxInternalBuilder withType(String type) { + this.type = type; + return this; + } + + public TxInternalBuilder withTraceId(String traceId) { + this.traceId = traceId; + return this; + } + + public TxInternalBuilder withIsError(int isError) { + this.isError = isError; + return this; + } + + public TxInternalBuilder withErrCode(String errCode) { + this.errCode = errCode; + return this; + } + + public TxInternal build() { + TxInternal txInternal = new TxInternal(); + txInternal.gas = this.gas; + txInternal.hash = this.hash; + txInternal.gasUsed = this.gasUsed; + txInternal.traceId = this.traceId; + txInternal.type = this.type; + txInternal.from = this.from; + txInternal.contractAddress = this.contractAddress; + txInternal.value = this.value; + txInternal.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + txInternal.errCode = this.errCode; + txInternal.blockNumber = this.blockNumber; + txInternal._timeStamp = this.timeStamp; + txInternal.isError = this.isError; + txInternal.to = this.to; + txInternal.input = this.input; + return txInternal; + } + } } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Wei.java b/src/main/java/io/goodforgod/api/etherscan/model/Wei.java index 224a1cf..85dd3ab 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Wei.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Wei.java @@ -9,7 +9,7 @@ */ public class Wei { - private BigInteger result; + private final BigInteger result; public Wei(BigInteger value) { this.result = value; diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java index 889cc0e..0e6ff3a 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java @@ -205,4 +205,164 @@ public String toString() { ", transactions=" + transactions + '}'; } + + public static BlockProxyBuilder builder() { + return new BlockProxyBuilder(); + } + + public static final class BlockProxyBuilder { + + private Long number; + private String hash; + private String parentHash; + private String stateRoot; + private Long size; + private String difficulty; + private String totalDifficulty; + private LocalDateTime timestamp; + private String miner; + private String nonce; + private String extraData; + private String logsBloom; + private String mixHash; + private BigInteger gasUsed; + private BigInteger gasLimit; + private String sha3Uncles; + private List uncles; + private String receiptsRoot; + private String transactionsRoot; + private List transactions; + + private BlockProxyBuilder() {} + + public BlockProxyBuilder withNumber(Long number) { + this.number = number; + return this; + } + + public BlockProxyBuilder withHash(String hash) { + this.hash = hash; + return this; + } + + public BlockProxyBuilder withParentHash(String parentHash) { + this.parentHash = parentHash; + return this; + } + + public BlockProxyBuilder withStateRoot(String stateRoot) { + this.stateRoot = stateRoot; + return this; + } + + public BlockProxyBuilder withSize(Long size) { + this.size = size; + return this; + } + + public BlockProxyBuilder withDifficulty(String difficulty) { + this.difficulty = difficulty; + return this; + } + + public BlockProxyBuilder withTotalDifficulty(String totalDifficulty) { + this.totalDifficulty = totalDifficulty; + return this; + } + + public BlockProxyBuilder withTimestamp(LocalDateTime timestamp) { + this.timestamp = timestamp; + return this; + } + + public BlockProxyBuilder withMiner(String miner) { + this.miner = miner; + return this; + } + + public BlockProxyBuilder withNonce(String nonce) { + this.nonce = nonce; + return this; + } + + public BlockProxyBuilder withExtraData(String extraData) { + this.extraData = extraData; + return this; + } + + public BlockProxyBuilder withLogsBloom(String logsBloom) { + this.logsBloom = logsBloom; + return this; + } + + public BlockProxyBuilder withMixHash(String mixHash) { + this.mixHash = mixHash; + return this; + } + + public BlockProxyBuilder withGasUsed(BigInteger gasUsed) { + this.gasUsed = gasUsed; + return this; + } + + public BlockProxyBuilder withGasLimit(BigInteger gasLimit) { + this.gasLimit = gasLimit; + return this; + } + + public BlockProxyBuilder withSha3Uncles(String sha3Uncles) { + this.sha3Uncles = sha3Uncles; + return this; + } + + public BlockProxyBuilder withUncles(List uncles) { + this.uncles = uncles; + return this; + } + + public BlockProxyBuilder withReceiptsRoot(String receiptsRoot) { + this.receiptsRoot = receiptsRoot; + return this; + } + + public BlockProxyBuilder withTransactionsRoot(String transactionsRoot) { + this.transactionsRoot = transactionsRoot; + return this; + } + + public BlockProxyBuilder withTransactions(List transactions) { + this.transactions = transactions; + return this; + } + + public BlockProxy build() { + BlockProxy blockProxy = new BlockProxy(); + blockProxy.mixHash = this.mixHash; + blockProxy.totalDifficulty = this.totalDifficulty; + blockProxy.nonce = this.nonce; + blockProxy._gasUsed = this.gasUsed; + blockProxy.uncles = this.uncles; + blockProxy.transactionsRoot = this.transactionsRoot; + blockProxy.number = String.valueOf(this.number); + blockProxy.logsBloom = this.logsBloom; + blockProxy.receiptsRoot = this.receiptsRoot; + blockProxy._gasLimit = this.gasLimit; + blockProxy.hash = this.hash; + blockProxy.parentHash = this.parentHash; + blockProxy._size = this.size; + blockProxy.gasLimit = String.valueOf(this.gasLimit); + blockProxy.difficulty = this.difficulty; + blockProxy.gasUsed = String.valueOf(this.gasUsed); + blockProxy.size = String.valueOf(this.size); + blockProxy.extraData = this.extraData; + blockProxy.stateRoot = this.stateRoot; + blockProxy._timestamp = this.timestamp; + blockProxy.sha3Uncles = this.sha3Uncles; + blockProxy.miner = this.miner; + blockProxy.timestamp = String.valueOf(this.timestamp.toEpochSecond(ZoneOffset.UTC)); + blockProxy.transactions = this.transactions; + blockProxy._number = this.number; + return blockProxy; + } + } } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java index f4f7845..73a21b6 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java @@ -149,4 +149,107 @@ public String toString() { ", logsBloom='" + logsBloom + '\'' + '}'; } + + public static ReceiptProxyBuilder builder() { + return new ReceiptProxyBuilder(); + } + + public static final class ReceiptProxyBuilder { + + private String root; + private String from; + private String to; + private Long blockNumber; + private String blockHash; + private String transactionHash; + private Long transactionIndex; + private BigInteger gasUsed; + private BigInteger cumulativeGasUsed; + private String contractAddress; + private List logs; + private String logsBloom; + + private ReceiptProxyBuilder() {} + + public ReceiptProxyBuilder withRoot(String root) { + this.root = root; + return this; + } + + public ReceiptProxyBuilder withFrom(String from) { + this.from = from; + return this; + } + + public ReceiptProxyBuilder withTo(String to) { + this.to = to; + return this; + } + + public ReceiptProxyBuilder withBlockNumber(Long blockNumber) { + this.blockNumber = blockNumber; + return this; + } + + public ReceiptProxyBuilder withBlockHash(String blockHash) { + this.blockHash = blockHash; + return this; + } + + public ReceiptProxyBuilder withTransactionHash(String transactionHash) { + this.transactionHash = transactionHash; + return this; + } + + public ReceiptProxyBuilder withTransactionIndex(Long transactionIndex) { + this.transactionIndex = transactionIndex; + return this; + } + + public ReceiptProxyBuilder withGasUsed(BigInteger gasUsed) { + this.gasUsed = gasUsed; + return this; + } + + public ReceiptProxyBuilder withCumulativeGasUsed(BigInteger cumulativeGasUsed) { + this.cumulativeGasUsed = cumulativeGasUsed; + return this; + } + + public ReceiptProxyBuilder withContractAddress(String contractAddress) { + this.contractAddress = contractAddress; + return this; + } + + public ReceiptProxyBuilder withLogs(List logs) { + this.logs = logs; + return this; + } + + public ReceiptProxyBuilder withLogsBloom(String logsBloom) { + this.logsBloom = logsBloom; + return this; + } + + public ReceiptProxy build() { + ReceiptProxy receiptProxy = new ReceiptProxy(); + receiptProxy.logsBloom = this.logsBloom; + receiptProxy.transactionHash = this.transactionHash; + receiptProxy.blockNumber = String.valueOf(this.blockNumber); + receiptProxy.from = this.from; + receiptProxy._transactionIndex = this.transactionIndex; + receiptProxy.blockHash = this.blockHash; + receiptProxy.root = this.root; + receiptProxy.contractAddress = this.contractAddress; + receiptProxy.gasUsed = String.valueOf(this.gasUsed); + receiptProxy._gasUsed = this.gasUsed; + receiptProxy.logs = this.logs; + receiptProxy.cumulativeGasUsed = String.valueOf(this.cumulativeGasUsed); + receiptProxy.to = this.to; + receiptProxy.transactionIndex = String.valueOf(this.transactionIndex); + receiptProxy._blockNumber = this.blockNumber; + receiptProxy._cumulativeGasUsed = this.cumulativeGasUsed; + return receiptProxy; + } + } } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java index cf3199b..52fe41b 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java @@ -163,4 +163,122 @@ public String toString() { ", _blockNumber=" + _blockNumber + '}'; } + + public static TxProxyBuilder builder() { + return new TxProxyBuilder(); + } + + public static final class TxProxyBuilder { + + private String to; + private String hash; + private Long transactionIndex; + private String from; + private String v; + private String input; + private String s; + private String r; + private Long nonce; + private String value; + private BigInteger gas; + private BigInteger gasPrice; + private String blockHash; + private Long blockNumber; + + private TxProxyBuilder() {} + + public TxProxyBuilder withTo(String to) { + this.to = to; + return this; + } + + public TxProxyBuilder withHash(String hash) { + this.hash = hash; + return this; + } + + public TxProxyBuilder withTransactionIndex(Long transactionIndex) { + this.transactionIndex = transactionIndex; + return this; + } + + public TxProxyBuilder withFrom(String from) { + this.from = from; + return this; + } + + public TxProxyBuilder withV(String v) { + this.v = v; + return this; + } + + public TxProxyBuilder withInput(String input) { + this.input = input; + return this; + } + + public TxProxyBuilder withS(String s) { + this.s = s; + return this; + } + + public TxProxyBuilder withR(String r) { + this.r = r; + return this; + } + + public TxProxyBuilder withNonce(Long nonce) { + this.nonce = nonce; + return this; + } + + public TxProxyBuilder withValue(String value) { + this.value = value; + return this; + } + + public TxProxyBuilder withGas(BigInteger gas) { + this.gas = gas; + return this; + } + + public TxProxyBuilder withGasPrice(BigInteger gasPrice) { + this.gasPrice = gasPrice; + return this; + } + + public TxProxyBuilder withBlockHash(String blockHash) { + this.blockHash = blockHash; + return this; + } + + public TxProxyBuilder withBlockNumber(Long blockNumber) { + this.blockNumber = blockNumber; + return this; + } + + public TxProxy build() { + TxProxy txProxy = new TxProxy(); + txProxy.input = this.input; + txProxy.gas = String.valueOf(this.gas); + txProxy._gas = this.gas; + txProxy.s = this.s; + txProxy.blockHash = this.blockHash; + txProxy.to = this.to; + txProxy.r = this.r; + txProxy.transactionIndex = String.valueOf(this.transactionIndex); + txProxy._nonce = this.nonce; + txProxy.value = this.value; + txProxy.v = this.v; + txProxy.from = this.from; + txProxy.nonce = String.valueOf(this.nonce); + txProxy._gasPrice = this.gasPrice; + txProxy._transactionIndex = this.transactionIndex; + txProxy.blockNumber = String.valueOf(this.blockNumber); + txProxy._blockNumber = this.blockNumber; + txProxy.hash = this.hash; + txProxy.gasPrice = String.valueOf(this.gasPrice); + return txProxy; + } + } } From fe444f4ed8a16922cf06b31610947c95365ba22f Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 14 May 2023 01:12:58 +0300 Subject: [PATCH 16/61] [2.0.0-SNAPSHOT] GasEstimate added to GasTrackerAPI#estimate --- .../api/etherscan/GasTrackerAPI.java | 13 ++++- .../api/etherscan/GasTrackerAPIProvider.java | 16 +++++++ .../api/etherscan/model/GasEstimate.java | 47 +++++++++++++++++++ .../model/response/GasEstimateResponseTO.java | 14 ++++++ 4 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 src/main/java/io/goodforgod/api/etherscan/model/GasEstimate.java create mode 100644 src/main/java/io/goodforgod/api/etherscan/model/response/GasEstimateResponseTO.java diff --git a/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPI.java b/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPI.java index d49e14f..355d62a 100644 --- a/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPI.java +++ b/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPI.java @@ -1,7 +1,9 @@ package io.goodforgod.api.etherscan; import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.model.GasEstimate; import io.goodforgod.api.etherscan.model.GasOracle; +import io.goodforgod.api.etherscan.model.Wei; import org.jetbrains.annotations.NotNull; /** @@ -14,7 +16,16 @@ public interface GasTrackerAPI { /** - * GasOracle details + * Returns the estimated time, in seconds, for a transaction to be confirmed on the blockchain. + * + * @return fast, suggested gas price + * @throws EtherScanException parent exception class + */ + @NotNull + GasEstimate estimate(@NotNull Wei wei) throws EtherScanException; + + /** + * Returns the current Safe, Proposed and Fast gas prices. * * @return fast, suggested gas price * @throws EtherScanException parent exception class diff --git a/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java index faa01e5..d4ec276 100644 --- a/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java @@ -4,7 +4,10 @@ import io.goodforgod.api.etherscan.error.EtherScanResponseException; import io.goodforgod.api.etherscan.executor.EthHttpClient; import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import io.goodforgod.api.etherscan.model.GasEstimate; import io.goodforgod.api.etherscan.model.GasOracle; +import io.goodforgod.api.etherscan.model.Wei; +import io.goodforgod.api.etherscan.model.response.GasEstimateResponseTO; import io.goodforgod.api.etherscan.model.response.GasOracleResponseTO; import org.jetbrains.annotations.NotNull; @@ -18,6 +21,9 @@ final class GasTrackerAPIProvider extends BasicProvider implements GasTrackerAPI { private static final String ACT_GAS_ORACLE_PARAM = ACT_PREFIX + "gasoracle"; + private static final String ACT_GAS_ESTIMATE_PARAM = ACT_PREFIX + "gasestimate"; + + private static final String GASPRICE_PARAM = "&gasprice="; GasTrackerAPIProvider(RequestQueueManager queue, String baseUrl, @@ -25,6 +31,16 @@ final class GasTrackerAPIProvider extends BasicProvider implements GasTrackerAPI super(queue, "gastracker", baseUrl, ethHttpClient); } + @Override + public @NotNull GasEstimate estimate(@NotNull Wei wei) throws EtherScanException { + final String urlParams = ACT_GAS_ESTIMATE_PARAM + GASPRICE_PARAM + wei.getValue().toString(); + final GasEstimateResponseTO response = getRequest(urlParams, GasEstimateResponseTO.class); + if (response.getStatus() != 1) + throw new EtherScanResponseException(response); + + return new GasEstimate(Long.parseLong(response.getResult())); + } + @NotNull @Override public GasOracle oracle() throws EtherScanException { diff --git a/src/main/java/io/goodforgod/api/etherscan/model/GasEstimate.java b/src/main/java/io/goodforgod/api/etherscan/model/GasEstimate.java new file mode 100644 index 0000000..7f1e61d --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/GasEstimate.java @@ -0,0 +1,47 @@ +package io.goodforgod.api.etherscan.model; + +import java.time.Duration; +import java.util.Objects; + +/** + * @author GoodforGod + * @since 14.05.2023 + */ +public class GasEstimate { + + private final Duration duration; + + public GasEstimate(long durationInSeconds) { + this.duration = Duration.ofSeconds(durationInSeconds); + } + + public GasEstimate(Duration duration) { + this.duration = duration; + } + + public Duration getDuration() { + return duration; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof GasEstimate)) + return false; + GasEstimate that = (GasEstimate) o; + return Objects.equals(duration, that.duration); + } + + @Override + public int hashCode() { + return Objects.hash(duration); + } + + @Override + public String toString() { + return "GasEstimate{" + + "duration=" + duration + + '}'; + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/GasEstimateResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/GasEstimateResponseTO.java new file mode 100644 index 0000000..96432f3 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/GasEstimateResponseTO.java @@ -0,0 +1,14 @@ +package io.goodforgod.api.etherscan.model.response; + +/** + * @author Abhay Gupta + * @since 14.11.2022 + */ +public class GasEstimateResponseTO extends BaseResponseTO { + + private String result; + + public String getResult() { + return result; + } +} From 192cb5b872e447c7f611044ca215e53619a07547 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 14 May 2023 01:23:43 +0300 Subject: [PATCH 17/61] [2.0.0-SNAPSHOT] RequestQueueManager values renamed --- .../java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java | 2 +- .../api/etherscan/manager/RequestQueueManager.java | 5 +++-- src/test/java/io/goodforgod/api/etherscan/ApiRunner.java | 2 +- .../io/goodforgod/api/etherscan/proxy/ProxyBlockApiTest.java | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java b/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java index d36d385..6571121 100644 --- a/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java +++ b/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java @@ -20,7 +20,7 @@ final class EthScanAPIBuilder implements EtherScanAPI.Builder { private String apiKey = DEFAULT_KEY; private EthNetwork ethNetwork = EthNetworks.MAINNET; - private RequestQueueManager queueManager = RequestQueueManager.DEFAULT_KEY_QUEUE; + private RequestQueueManager queueManager = RequestQueueManager.DEFAULT; private Supplier ethHttpClientSupplier = DEFAULT_SUPPLIER; @NotNull diff --git a/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java b/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java index 7472c3f..d568601 100644 --- a/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java +++ b/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java @@ -12,9 +12,10 @@ */ public interface RequestQueueManager extends AutoCloseable { - RequestQueueManager DEFAULT_KEY_QUEUE = new SemaphoreRequestQueueManager(1, Duration.ofMillis(5050L), + RequestQueueManager DEFAULT = new SemaphoreRequestQueueManager(1, Duration.ofMillis(5050L), Duration.ofMillis(5050L), 0); - RequestQueueManager PERSONAL_KEY_QUEUE = new SemaphoreRequestQueueManager(5, Duration.ofMillis(1050L), + + RequestQueueManager PERSONAL = new SemaphoreRequestQueueManager(5, Duration.ofMillis(1050L), Duration.ofMillis(1050L), 5); /** diff --git a/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java b/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java index 3b9cbe2..cd4a657 100644 --- a/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java +++ b/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java @@ -23,7 +23,7 @@ public class ApiRunner extends Assertions { : key; final RequestQueueManager queueManager = (DEFAULT_KEY.equals(apiKey)) - ? RequestQueueManager.DEFAULT_KEY_QUEUE + ? RequestQueueManager.DEFAULT : new SemaphoreRequestQueueManager(1, Duration.ofMillis(1200L), Duration.ofMillis(1200L), 0); api = EtherScanAPI.builder().withApiKey(ApiRunner.apiKey).withNetwork(EthNetworks.MAINNET).withQueue(queueManager) diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTest.java index 64e3fe6..e31aab8 100644 --- a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTest.java @@ -17,7 +17,7 @@ class ProxyBlockApiTest extends ApiRunner { private final EtherScanAPI api; ProxyBlockApiTest() { - final RequestQueueManager queueManager = RequestQueueManager.DEFAULT_KEY_QUEUE; + final RequestQueueManager queueManager = RequestQueueManager.DEFAULT; this.api = EtherScanAPI.builder().withApiKey(getApiKey()).withNetwork(EthNetworks.MAINNET).withQueue(queueManager) .build(); } From 0cafb6df4518156bc914cb8adb40e41ff5fb3d85 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 14 May 2023 02:17:12 +0300 Subject: [PATCH 18/61] [2.0.0-SNAPSHOT] TxErc1155 added AccountAPI#txsErc1155 added AccountAPI#txsErc721 with contract added --- .../goodforgod/api/etherscan/AccountAPI.java | 72 +++++- .../api/etherscan/AccountAPIProvider.java | 122 ++++++++-- .../goodforgod/api/etherscan/EthNetwork.java | 3 + .../api/etherscan/model/BaseTx.java | 41 +--- .../io/goodforgod/api/etherscan/model/Tx.java | 33 +-- .../api/etherscan/model/TxErc1155.java | 230 ++++++++++++++++++ .../model/{TxERC20.java => TxErc20.java} | 12 +- .../model/{TxERC721.java => TxErc721.java} | 26 +- .../api/etherscan/model/TxInternal.java | 22 +- .../model/response/TxERC20ResponseTO.java | 11 - .../model/response/TxERC721ResponseTO.java | 11 - .../model/response/TxErc1155ResponseTO.java | 11 + .../model/response/TxErc20ResponseTO.java | 11 + .../model/response/TxErc721ResponseTO.java | 11 + .../api/etherscan/util/BasicUtils.java | 2 +- ...ERC20Test.java => AccountTxErc20Test.java} | 18 +- .../account/AccountTxRc1155TokenTest.java | 81 ++++++ .../account/AccountTxRc721TokenTest.java | 16 +- 18 files changed, 577 insertions(+), 156 deletions(-) create mode 100644 src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java rename src/main/java/io/goodforgod/api/etherscan/model/{TxERC20.java => TxErc20.java} (96%) rename src/main/java/io/goodforgod/api/etherscan/model/{TxERC721.java => TxErc721.java} (93%) delete mode 100644 src/main/java/io/goodforgod/api/etherscan/model/response/TxERC20ResponseTO.java delete mode 100644 src/main/java/io/goodforgod/api/etherscan/model/response/TxERC721ResponseTO.java create mode 100644 src/main/java/io/goodforgod/api/etherscan/model/response/TxErc1155ResponseTO.java create mode 100644 src/main/java/io/goodforgod/api/etherscan/model/response/TxErc20ResponseTO.java create mode 100644 src/main/java/io/goodforgod/api/etherscan/model/response/TxErc721ResponseTO.java rename src/test/java/io/goodforgod/api/etherscan/account/{AccountTxERC20Test.java => AccountTxErc20Test.java} (81%) create mode 100644 src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc1155TokenTest.java diff --git a/src/main/java/io/goodforgod/api/etherscan/AccountAPI.java b/src/main/java/io/goodforgod/api/etherscan/AccountAPI.java index 40da2eb..294fb2a 100644 --- a/src/main/java/io/goodforgod/api/etherscan/AccountAPI.java +++ b/src/main/java/io/goodforgod/api/etherscan/AccountAPI.java @@ -101,13 +101,13 @@ public interface AccountAPI { * @throws EtherScanException parent exception class */ @NotNull - List txsERC20(String address, long startBlock, long endBlock) throws EtherScanException; + List txsErc20(String address, long startBlock, long endBlock) throws EtherScanException; @NotNull - List txsERC20(String address, long startBlock) throws EtherScanException; + List txsErc20(String address, long startBlock) throws EtherScanException; @NotNull - List txsERC20(String address) throws EtherScanException; + List txsErc20(String address) throws EtherScanException; /** * All ERC-20 token txs for given address and contract address @@ -120,13 +120,13 @@ public interface AccountAPI { * @throws EtherScanException parent exception class */ @NotNull - List txsERC20(String address, String contractAddress, long startBlock, long endBlock) throws EtherScanException; + List txsErc20(String address, String contractAddress, long startBlock, long endBlock) throws EtherScanException; @NotNull - List txsERC20(String address, String contractAddress, long startBlock) throws EtherScanException; + List txsErc20(String address, String contractAddress, long startBlock) throws EtherScanException; @NotNull - List txsERC20(String address, String contractAddress) throws EtherScanException; + List txsErc20(String address, String contractAddress) throws EtherScanException; /** * All ERC-721 (NFT) token txs for given address @@ -138,13 +138,67 @@ public interface AccountAPI { * @throws EtherScanException parent exception class */ @NotNull - List txsERC721(String address, long startBlock, long endBlock) throws EtherScanException; + List txsErc721(String address, long startBlock, long endBlock) throws EtherScanException; @NotNull - List txsERC721(String address, long startBlock) throws EtherScanException; + List txsErc721(String address, long startBlock) throws EtherScanException; @NotNull - List txsERC721(String address) throws EtherScanException; + List txsErc721(String address) throws EtherScanException; + + /** + * All ERC-721 (NFT) token txs for given address + * + * @param address get txs for + * @param startBlock tx from this blockNumber + * @param endBlock tx to this blockNumber + * @return txs for address + * @throws EtherScanException parent exception class + */ + @NotNull + List txsErc721(String address, String contractAddress, long startBlock, long endBlock) throws EtherScanException; + + @NotNull + List txsErc721(String address, String contractAddress, long startBlock) throws EtherScanException; + + @NotNull + List txsErc721(String address, String contractAddress) throws EtherScanException; + + /** + * All ERC-721 (NFT) token txs for given address + * + * @param address get txs for + * @param startBlock tx from this blockNumber + * @param endBlock tx to this blockNumber + * @return txs for address + * @throws EtherScanException parent exception class + */ + @NotNull + List txsErc1155(String address, long startBlock, long endBlock) throws EtherScanException; + + @NotNull + List txsErc1155(String address, long startBlock) throws EtherScanException; + + @NotNull + List txsErc1155(String address) throws EtherScanException; + + /** + * All ERC-721 (NFT) token txs for given address + * + * @param address get txs for + * @param startBlock tx from this blockNumber + * @param endBlock tx to this blockNumber + * @return txs for address + * @throws EtherScanException parent exception class + */ + @NotNull + List txsErc1155(String address, String contractAddress, long startBlock, long endBlock) throws EtherScanException; + + @NotNull + List txsErc1155(String address, String contractAddress, long startBlock) throws EtherScanException; + + @NotNull + List txsErc1155(String address, String contractAddress) throws EtherScanException; /** * All blocks mined by address diff --git a/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java index 9160bb4..9382618 100644 --- a/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java @@ -30,8 +30,9 @@ final class AccountAPIProvider extends BasicProvider implements AccountAPI { private static final String ACT_BALANCE_MULTI_ACTION = ACT_PREFIX + "balancemulti"; private static final String ACT_TX_ACTION = ACT_PREFIX + "txlist"; private static final String ACT_TX_INTERNAL_ACTION = ACT_PREFIX + "txlistinternal"; - private static final String ACT_TX_TOKEN_ACTION = ACT_PREFIX + "tokentx"; - private static final String ACT_TX_NFT_TOKEN_ACTION = ACT_PREFIX + "tokennfttx"; + private static final String ACT_TX_ERC20_ACTION = ACT_PREFIX + "tokentx"; + private static final String ACT_TX_ERC721_ACTION = ACT_PREFIX + "tokennfttx"; + private static final String ACT_TX_ERC1155_ACTION = ACT_PREFIX + "token1155tx"; private static final String ACT_MINED_ACTION = ACT_PREFIX + "getminedblocks"; private static final String BLOCK_TYPE_PARAM = "&blocktype=blocks"; @@ -145,8 +146,7 @@ public List txs(String address, long startBlock, long endBlock) throws Ether * @param responseListTO type * @return List of T values */ - private List getRequestUsingOffset(final String urlParams, - Class tClass) + private > List getRequestUsingOffset(final String urlParams, Class tClass) throws EtherScanException { final List result = new ArrayList<>(); int page = 1; @@ -208,81 +208,153 @@ public List txsInternalByHash(String txhash) throws EtherScanExcepti @NotNull @Override - public List txsERC20(String address) throws EtherScanException { - return txsERC20(address, MIN_START_BLOCK); + public List txsErc20(String address) throws EtherScanException { + return txsErc20(address, MIN_START_BLOCK); } @NotNull @Override - public List txsERC20(String address, long startBlock) throws EtherScanException { - return txsERC20(address, startBlock, MAX_END_BLOCK); + public List txsErc20(String address, long startBlock) throws EtherScanException { + return txsErc20(address, startBlock, MAX_END_BLOCK); } @NotNull @Override - public List txsERC20(String address, long startBlock, long endBlock) throws EtherScanException { + public List txsErc20(String address, long startBlock, long endBlock) throws EtherScanException { BasicUtils.validateAddress(address); final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); - final String urlParams = ACT_TX_TOKEN_ACTION + PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX + final String urlParams = ACT_TX_ERC20_ACTION + PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX + ADDRESS_PARAM + address + START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end() + SORT_ASC_PARAM; - return getRequestUsingOffset(urlParams, TxERC20ResponseTO.class); + return getRequestUsingOffset(urlParams, TxErc20ResponseTO.class); } @NotNull @Override - public List txsERC20(String address, String contractAddress) throws EtherScanException { - return txsERC20(address, contractAddress, MIN_START_BLOCK); + public List txsErc20(String address, String contractAddress) throws EtherScanException { + return txsErc20(address, contractAddress, MIN_START_BLOCK); } @NotNull @Override - public List txsERC20(String address, String contractAddress, long startBlock) throws EtherScanException { - return txsERC20(address, contractAddress, startBlock, MAX_END_BLOCK); + public List txsErc20(String address, String contractAddress, long startBlock) throws EtherScanException { + return txsErc20(address, contractAddress, startBlock, MAX_END_BLOCK); } @NotNull @Override - public List txsERC20(String address, String contractAddress, long startBlock, long endBlock) + public List txsErc20(String address, String contractAddress, long startBlock, long endBlock) throws EtherScanException { BasicUtils.validateAddress(address); final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); final String offsetParam = PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX; final String blockParam = START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end(); - final String urlParams = ACT_TX_TOKEN_ACTION + offsetParam + ADDRESS_PARAM + address + final String urlParams = ACT_TX_ERC20_ACTION + offsetParam + ADDRESS_PARAM + address + CONTRACT_PARAM + contractAddress + blockParam + SORT_ASC_PARAM; - return getRequestUsingOffset(urlParams, TxERC20ResponseTO.class); + return getRequestUsingOffset(urlParams, TxErc20ResponseTO.class); } @NotNull @Override - public List txsERC721(String address) throws EtherScanException { - return txsERC721(address, MIN_START_BLOCK); + public List txsErc721(String address) throws EtherScanException { + return txsErc721(address, MIN_START_BLOCK); } @NotNull @Override - public List txsERC721(String address, long startBlock) throws EtherScanException { - return txsERC721(address, startBlock, MAX_END_BLOCK); + public List txsErc721(String address, long startBlock) throws EtherScanException { + return txsErc721(address, startBlock, MAX_END_BLOCK); } @NotNull @Override - public List txsERC721(String address, long startBlock, long endBlock) throws EtherScanException { + public List txsErc721(String address, long startBlock, long endBlock) throws EtherScanException { BasicUtils.validateAddress(address); final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); - final String urlParams = ACT_TX_NFT_TOKEN_ACTION + PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX + final String urlParams = ACT_TX_ERC721_ACTION + PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX + ADDRESS_PARAM + address + START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end() + SORT_ASC_PARAM; - return getRequestUsingOffset(urlParams, TxERC20ResponseTO.class); + return getRequestUsingOffset(urlParams, TxErc721ResponseTO.class); + } + + @Override + public @NotNull List txsErc721(String address, String contractAddress, long startBlock, long endBlock) + throws EtherScanException { + BasicUtils.validateAddress(address); + final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); + + final String offsetParam = PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX; + final String blockParam = START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end(); + final String urlParams = ACT_TX_ERC721_ACTION + offsetParam + ADDRESS_PARAM + address + + CONTRACT_PARAM + contractAddress + blockParam + SORT_ASC_PARAM; + + return getRequestUsingOffset(urlParams, TxErc721ResponseTO.class); + } + + @Override + public @NotNull List txsErc721(String address, String contractAddress, long startBlock) throws EtherScanException { + return txsErc721(address, contractAddress, startBlock, MAX_END_BLOCK); + } + + @Override + public @NotNull List txsErc721(String address, String contractAddress) throws EtherScanException { + return txsErc721(address, contractAddress, MIN_START_BLOCK); + } + + @Override + public @NotNull List txsErc1155(String address, long startBlock, long endBlock) throws EtherScanException { + BasicUtils.validateAddress(address); + final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); + + final String urlParams = ACT_TX_ERC1155_ACTION + PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX + + ADDRESS_PARAM + address + + START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end() + + SORT_ASC_PARAM; + + return getRequestUsingOffset(urlParams, TxErc1155ResponseTO.class); + } + + @Override + public @NotNull List txsErc1155(String address, long startBlock) throws EtherScanException { + return txsErc1155(address, startBlock, MAX_END_BLOCK); + } + + @Override + public @NotNull List txsErc1155(String address) throws EtherScanException { + return txsErc1155(address, MIN_START_BLOCK); + } + + @Override + public @NotNull List txsErc1155(String address, String contractAddress, long startBlock, long endBlock) + throws EtherScanException { + BasicUtils.validateAddress(address); + final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); + + final String offsetParam = PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX; + final String blockParam = START_BLOCK_PARAM + blocks.start() + END_BLOCK_PARAM + blocks.end(); + final String urlParams = ACT_TX_ERC1155_ACTION + offsetParam + ADDRESS_PARAM + address + + CONTRACT_PARAM + contractAddress + blockParam + SORT_ASC_PARAM; + + return getRequestUsingOffset(urlParams, TxErc1155ResponseTO.class); + } + + @Override + public @NotNull List txsErc1155(String address, String contractAddress, long startBlock) + throws EtherScanException { + return txsErc1155(address, contractAddress, startBlock, MAX_END_BLOCK); + } + + @Override + public @NotNull List txsErc1155(String address, String contractAddress) throws EtherScanException { + return txsErc1155(address, contractAddress, MIN_START_BLOCK); } @NotNull diff --git a/src/main/java/io/goodforgod/api/etherscan/EthNetwork.java b/src/main/java/io/goodforgod/api/etherscan/EthNetwork.java index ce0d929..96d8d0b 100644 --- a/src/main/java/io/goodforgod/api/etherscan/EthNetwork.java +++ b/src/main/java/io/goodforgod/api/etherscan/EthNetwork.java @@ -9,6 +9,9 @@ */ public interface EthNetwork { + /** + * @return URI for network domain like EtherScan API + */ @NotNull URI domain(); } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/BaseTx.java b/src/main/java/io/goodforgod/api/etherscan/model/BaseTx.java index e159d3b..c66e60f 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/BaseTx.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/BaseTx.java @@ -20,7 +20,6 @@ abstract class BaseTx { String hash; String from; String to; - BigInteger value; String contractAddress; String input; BigInteger gas; @@ -49,10 +48,6 @@ public String getTo() { return to; } - public BigInteger getValue() { - return value; - } - public String getContractAddress() { return contractAddress; } @@ -76,41 +71,16 @@ public boolean equals(Object o) { return true; if (!(o instanceof BaseTx)) return false; - BaseTx baseTx = (BaseTx) o; - - if (blockNumber != baseTx.blockNumber) - return false; - if (!Objects.equals(timeStamp, baseTx.timeStamp)) - return false; - if (!Objects.equals(hash, baseTx.hash)) - return false; - if (!Objects.equals(from, baseTx.from)) - return false; - if (!Objects.equals(to, baseTx.to)) - return false; - return Objects.equals(value, baseTx.value); + return blockNumber == baseTx.blockNumber && Objects.equals(timeStamp, baseTx.timeStamp) + && Objects.equals(hash, baseTx.hash) && Objects.equals(from, baseTx.from) && Objects.equals(to, baseTx.to) + && Objects.equals(contractAddress, baseTx.contractAddress) && Objects.equals(input, baseTx.input) + && Objects.equals(gas, baseTx.gas) && Objects.equals(gasUsed, baseTx.gasUsed); } @Override public int hashCode() { - int result = (int) (blockNumber ^ (blockNumber >>> 32)); - result = 31 * result + (timeStamp != null - ? timeStamp.hashCode() - : 0); - result = 31 * result + (hash != null - ? hash.hashCode() - : 0); - result = 31 * result + (from != null - ? from.hashCode() - : 0); - result = 31 * result + (to != null - ? to.hashCode() - : 0); - result = 31 * result + (value != null - ? value.hashCode() - : 0); - return result; + return Objects.hash(blockNumber, timeStamp, hash, from, to, contractAddress, input, gas, gasUsed); } @Override @@ -121,7 +91,6 @@ public String toString() { ", hash='" + hash + '\'' + ", from='" + from + '\'' + ", to='" + to + '\'' + - ", value=" + value + ", contractAddress='" + contractAddress + '\'' + ", input='" + input + '\'' + ", gas=" + gas + diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Tx.java b/src/main/java/io/goodforgod/api/etherscan/model/Tx.java index cc9be41..65c24ba 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Tx.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Tx.java @@ -12,6 +12,7 @@ */ public class Tx extends BaseTx { + private BigInteger value; private long nonce; private String blockHash; private int transactionIndex; @@ -22,6 +23,10 @@ public class Tx extends BaseTx { private String txreceipt_status; // + public BigInteger getValue() { + return value; + } + public long getNonce() { return nonce; } @@ -59,40 +64,28 @@ public long getConfirmations() { public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) + if (!(o instanceof Tx)) return false; if (!super.equals(o)) return false; - Tx tx = (Tx) o; - - if (nonce != tx.nonce) - return false; - if (transactionIndex != tx.transactionIndex) - return false; - if (!Objects.equals(blockHash, tx.blockHash)) - return false; - return Objects.equals(isError, tx.isError); + return nonce == tx.nonce && transactionIndex == tx.transactionIndex && confirmations == tx.confirmations + && Objects.equals(value, tx.value) && Objects.equals(blockHash, tx.blockHash) + && Objects.equals(gasPrice, tx.gasPrice) && Objects.equals(cumulativeGasUsed, tx.cumulativeGasUsed) + && Objects.equals(isError, tx.isError) && Objects.equals(txreceipt_status, tx.txreceipt_status); } @Override public int hashCode() { - int result = super.hashCode(); - result = 31 * result + (int) (nonce ^ (nonce >>> 32)); - result = 31 * result + (blockHash != null - ? blockHash.hashCode() - : 0); - result = 31 * result + transactionIndex; - result = 31 * result + (isError != null - ? isError.hashCode() - : 0); - return result; + return Objects.hash(super.hashCode(), value, nonce, blockHash, transactionIndex, gasPrice, cumulativeGasUsed, + confirmations, isError, txreceipt_status); } @Override public String toString() { return "Tx{" + "nonce=" + nonce + + ", value='" + value + '\'' + ", blockHash='" + blockHash + '\'' + ", transactionIndex=" + transactionIndex + ", gasPrice=" + gasPrice + diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java b/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java new file mode 100644 index 0000000..e57af5f --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java @@ -0,0 +1,230 @@ +package io.goodforgod.api.etherscan.model; + +import java.math.BigInteger; +import java.time.LocalDateTime; +import java.time.ZoneOffset; + +/** + * @author GoodforGod + * @since 28.10.2018 + */ +public class TxErc1155 extends BaseTx { + + private long nonce; + private String blockHash; + private String tokenID; + private String tokenName; + private String tokenSymbol; + private String tokenValue; + private int transactionIndex; + private long gasPrice; + private long cumulativeGasUsed; + private long confirmations; + + // + public long getNonce() { + return nonce; + } + + public String getBlockHash() { + return blockHash; + } + + public String getTokenID() { + return tokenID; + } + + public String getTokenName() { + return tokenName; + } + + public String getTokenSymbol() { + return tokenSymbol; + } + + public String getTokenValue() { + return tokenValue; + } + + public int getTransactionIndex() { + return transactionIndex; + } + + public long getGasPrice() { + return gasPrice; + } + + public long getCumulativeGasUsed() { + return cumulativeGasUsed; + } + + public long getConfirmations() { + return confirmations; + } + // + + @Override + public String toString() { + return "TxERC721{" + + "nonce=" + nonce + + ", blockHash='" + blockHash + '\'' + + ", tokenID='" + tokenID + '\'' + + ", tokenName='" + tokenName + '\'' + + ", tokenSymbol='" + tokenSymbol + '\'' + + ", tokenValue='" + tokenValue + '\'' + + ", transactionIndex=" + transactionIndex + + ", gasPrice=" + gasPrice + + ", cumulativeGasUsed=" + cumulativeGasUsed + + ", confirmations=" + confirmations + + "} " + super.toString(); + } + + public static TxErc1155Builder builder() { + return new TxErc1155Builder(); + } + + public static final class TxErc1155Builder { + + private long blockNumber; + private LocalDateTime timeStamp; + private String hash; + private String from; + private String to; + private String contractAddress; + private String input; + private BigInteger gas; + private BigInteger gasUsed; + private long nonce; + private String blockHash; + private String tokenID; + private String tokenName; + private String tokenSymbol; + private String tokenValue; + private int transactionIndex; + private long gasPrice; + private long cumulativeGasUsed; + private long confirmations; + + private TxErc1155Builder() {} + + public TxErc1155Builder withBlockNumber(long blockNumber) { + this.blockNumber = blockNumber; + return this; + } + + public TxErc1155Builder withTimeStamp(LocalDateTime timeStamp) { + this.timeStamp = timeStamp; + return this; + } + + public TxErc1155Builder withHash(String hash) { + this.hash = hash; + return this; + } + + public TxErc1155Builder withFrom(String from) { + this.from = from; + return this; + } + + public TxErc1155Builder withTo(String to) { + this.to = to; + return this; + } + + public TxErc1155Builder withContractAddress(String contractAddress) { + this.contractAddress = contractAddress; + return this; + } + + public TxErc1155Builder withInput(String input) { + this.input = input; + return this; + } + + public TxErc1155Builder withGas(BigInteger gas) { + this.gas = gas; + return this; + } + + public TxErc1155Builder withGasUsed(BigInteger gasUsed) { + this.gasUsed = gasUsed; + return this; + } + + public TxErc1155Builder withNonce(long nonce) { + this.nonce = nonce; + return this; + } + + public TxErc1155Builder withBlockHash(String blockHash) { + this.blockHash = blockHash; + return this; + } + + public TxErc1155Builder withTokenID(String tokenID) { + this.tokenID = tokenID; + return this; + } + + public TxErc1155Builder withTokenName(String tokenName) { + this.tokenName = tokenName; + return this; + } + + public TxErc1155Builder withTokenSymbol(String tokenSymbol) { + this.tokenSymbol = tokenSymbol; + return this; + } + + public TxErc1155Builder withTokenDecimal(String tokenDecimal) { + this.tokenValue = tokenDecimal; + return this; + } + + public TxErc1155Builder withTransactionIndex(int transactionIndex) { + this.transactionIndex = transactionIndex; + return this; + } + + public TxErc1155Builder withGasPrice(long gasPrice) { + this.gasPrice = gasPrice; + return this; + } + + public TxErc1155Builder withCumulativeGasUsed(long cumulativeGasUsed) { + this.cumulativeGasUsed = cumulativeGasUsed; + return this; + } + + public TxErc1155Builder withConfirmations(long confirmations) { + this.confirmations = confirmations; + return this; + } + + public TxErc1155 build() { + TxErc1155 txERC721 = new TxErc1155(); + txERC721.gas = this.gas; + txERC721.tokenName = this.tokenName; + txERC721.hash = this.hash; + txERC721.gasUsed = this.gasUsed; + txERC721.nonce = this.nonce; + txERC721.from = this.from; + txERC721.gasPrice = this.gasPrice; + txERC721.contractAddress = this.contractAddress; + txERC721.cumulativeGasUsed = this.cumulativeGasUsed; + txERC721.tokenID = this.tokenID; + txERC721.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + txERC721.blockNumber = this.blockNumber; + txERC721._timeStamp = this.timeStamp; + txERC721.tokenValue = this.tokenValue; + txERC721.transactionIndex = this.transactionIndex; + txERC721.to = this.to; + txERC721.confirmations = this.confirmations; + txERC721.input = this.input; + txERC721.blockHash = this.blockHash; + txERC721.tokenSymbol = this.tokenSymbol; + return txERC721; + } + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxERC20.java b/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java similarity index 96% rename from src/main/java/io/goodforgod/api/etherscan/model/TxERC20.java rename to src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java index 42ffebe..da6f54f 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TxERC20.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java @@ -8,8 +8,9 @@ * @author GoodforGod * @since 28.10.2018 */ -public class TxERC20 extends BaseTx { +public class TxErc20 extends BaseTx { + private BigInteger value; private long nonce; private String blockHash; private String tokenName; @@ -29,6 +30,10 @@ public String getBlockHash() { return blockHash; } + public BigInteger getValue() { + return value; + } + public String getTokenName() { return tokenName; } @@ -63,6 +68,7 @@ public String toString() { return "TxERC20{" + "nonce=" + nonce + ", blockHash='" + blockHash + '\'' + + ", value='" + value + '\'' + ", tokenName='" + tokenName + '\'' + ", tokenSymbol='" + tokenSymbol + '\'' + ", tokenDecimal='" + tokenDecimal + '\'' + @@ -196,8 +202,8 @@ public TxERC20Builder withConfirmations(long confirmations) { return this; } - public TxERC20 build() { - TxERC20 txERC20 = new TxERC20(); + public TxErc20 build() { + TxErc20 txERC20 = new TxErc20(); txERC20.gas = this.gas; txERC20.tokenName = this.tokenName; txERC20.hash = this.hash; diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxERC721.java b/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java similarity index 93% rename from src/main/java/io/goodforgod/api/etherscan/model/TxERC721.java rename to src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java index 14777fc..548113c 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TxERC721.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java @@ -8,10 +8,11 @@ * @author GoodforGod * @since 28.10.2018 */ -public class TxERC721 extends BaseTx { +public class TxErc721 extends BaseTx { private long nonce; private String blockHash; + private String tokenID; private String tokenName; private String tokenSymbol; private String tokenDecimal; @@ -29,6 +30,10 @@ public String getBlockHash() { return blockHash; } + public String getTokenID() { + return tokenID; + } + public String getTokenName() { return tokenName; } @@ -63,6 +68,7 @@ public String toString() { return "TxERC721{" + "nonce=" + nonce + ", blockHash='" + blockHash + '\'' + + ", tokenID='" + tokenID + '\'' + ", tokenName='" + tokenName + '\'' + ", tokenSymbol='" + tokenSymbol + '\'' + ", tokenDecimal='" + tokenDecimal + '\'' + @@ -84,13 +90,13 @@ public static final class TxERC721Builder { private String hash; private String from; private String to; - private BigInteger value; private String contractAddress; private String input; private BigInteger gas; private BigInteger gasUsed; private long nonce; private String blockHash; + private String tokenID; private String tokenName; private String tokenSymbol; private String tokenDecimal; @@ -126,11 +132,6 @@ public TxERC721Builder withTo(String to) { return this; } - public TxERC721Builder withValue(BigInteger value) { - this.value = value; - return this; - } - public TxERC721Builder withContractAddress(String contractAddress) { this.contractAddress = contractAddress; return this; @@ -161,6 +162,11 @@ public TxERC721Builder withBlockHash(String blockHash) { return this; } + public TxERC721Builder withTokenID(String tokenID) { + this.tokenID = tokenID; + return this; + } + public TxERC721Builder withTokenName(String tokenName) { this.tokenName = tokenName; return this; @@ -196,8 +202,8 @@ public TxERC721Builder withConfirmations(long confirmations) { return this; } - public TxERC721 build() { - TxERC721 txERC721 = new TxERC721(); + public TxErc721 build() { + TxErc721 txERC721 = new TxErc721(); txERC721.gas = this.gas; txERC721.tokenName = this.tokenName; txERC721.hash = this.hash; @@ -207,7 +213,7 @@ public TxERC721 build() { txERC721.gasPrice = this.gasPrice; txERC721.contractAddress = this.contractAddress; txERC721.cumulativeGasUsed = this.cumulativeGasUsed; - txERC721.value = this.value; + txERC721.tokenID = this.tokenID; txERC721.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); txERC721.blockNumber = this.blockNumber; txERC721._timeStamp = this.timeStamp; diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java b/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java index f1d1edf..84e10b3 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java @@ -11,12 +11,17 @@ */ public class TxInternal extends BaseTx { + private BigInteger value; private String type; private String traceId; private int isError; private String errCode; // + public BigInteger getValue() { + return value; + } + public String getType() { return type; } @@ -48,24 +53,14 @@ public boolean equals(Object o) { return false; if (!super.equals(o)) return false; - TxInternal that = (TxInternal) o; - - if (!Objects.equals(traceId, that.traceId)) - return false; - return Objects.equals(errCode, that.errCode); + return isError == that.isError && Objects.equals(value, that.value) && Objects.equals(type, that.type) + && Objects.equals(traceId, that.traceId) && Objects.equals(errCode, that.errCode); } @Override public int hashCode() { - int result = super.hashCode(); - result = 31 * result + (traceId != null - ? traceId.hashCode() - : 0); - result = 31 * result + (errCode != null - ? errCode.hashCode() - : 0); - return result; + return Objects.hash(super.hashCode(), value, type, traceId, isError, errCode); } @Override @@ -73,6 +68,7 @@ public String toString() { return "TxInternal{" + "type='" + type + '\'' + ", traceId=" + traceId + + ", value=" + value + ", isError=" + isError + ", errCode='" + errCode + '\'' + "} " + super.toString(); diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/TxERC20ResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/TxERC20ResponseTO.java deleted file mode 100644 index f4814a5..0000000 --- a/src/main/java/io/goodforgod/api/etherscan/model/response/TxERC20ResponseTO.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.goodforgod.api.etherscan.model.response; - -import io.goodforgod.api.etherscan.model.TxERC20; - -/** - * @author GoodforGod - * @since 29.10.2018 - */ -public class TxERC20ResponseTO extends BaseListResponseTO { - -} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/TxERC721ResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/TxERC721ResponseTO.java deleted file mode 100644 index b4db8ef..0000000 --- a/src/main/java/io/goodforgod/api/etherscan/model/response/TxERC721ResponseTO.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.goodforgod.api.etherscan.model.response; - -import io.goodforgod.api.etherscan.model.TxERC20; - -/** - * @author GoodforGod - * @since 29.10.2018 - */ -public class TxERC721ResponseTO extends BaseListResponseTO { - -} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc1155ResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc1155ResponseTO.java new file mode 100644 index 0000000..994d2cd --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc1155ResponseTO.java @@ -0,0 +1,11 @@ +package io.goodforgod.api.etherscan.model.response; + +import io.goodforgod.api.etherscan.model.TxErc1155; + +/** + * @author GoodforGod + * @since 29.10.2018 + */ +public class TxErc1155ResponseTO extends BaseListResponseTO { + +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc20ResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc20ResponseTO.java new file mode 100644 index 0000000..d5d3f6e --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc20ResponseTO.java @@ -0,0 +1,11 @@ +package io.goodforgod.api.etherscan.model.response; + +import io.goodforgod.api.etherscan.model.TxErc20; + +/** + * @author GoodforGod + * @since 29.10.2018 + */ +public class TxErc20ResponseTO extends BaseListResponseTO { + +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc721ResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc721ResponseTO.java new file mode 100644 index 0000000..2a9403f --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc721ResponseTO.java @@ -0,0 +1,11 @@ +package io.goodforgod.api.etherscan.model.response; + +import io.goodforgod.api.etherscan.model.TxErc721; + +/** + * @author GoodforGod + * @since 29.10.2018 + */ +public class TxErc721ResponseTO extends BaseListResponseTO { + +} diff --git a/src/main/java/io/goodforgod/api/etherscan/util/BasicUtils.java b/src/main/java/io/goodforgod/api/etherscan/util/BasicUtils.java index 4522ff5..eda3ce2 100644 --- a/src/main/java/io/goodforgod/api/etherscan/util/BasicUtils.java +++ b/src/main/java/io/goodforgod/api/etherscan/util/BasicUtils.java @@ -20,7 +20,7 @@ public final class BasicUtils { private BasicUtils() {} - private static final int MAX_END_BLOCK = 999999999; + private static final int MAX_END_BLOCK = Integer.MAX_VALUE; private static final int MIN_START_BLOCK = 0; private static final Pattern ADDRESS_PATTERN = Pattern.compile("0x[a-zA-Z0-9]{40}"); diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxERC20Test.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxErc20Test.java similarity index 81% rename from src/test/java/io/goodforgod/api/etherscan/account/AccountTxERC20Test.java rename to src/test/java/io/goodforgod/api/etherscan/account/AccountTxErc20Test.java index bacf2e3..0a94289 100644 --- a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxERC20Test.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxErc20Test.java @@ -2,7 +2,7 @@ import io.goodforgod.api.etherscan.ApiRunner; import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; -import io.goodforgod.api.etherscan.model.TxERC20; +import io.goodforgod.api.etherscan.model.TxErc20; import java.util.List; import org.junit.jupiter.api.Test; @@ -10,11 +10,11 @@ * @author GoodforGod * @since 03.11.2018 */ -class AccountTxERC20Test extends ApiRunner { +class AccountTxErc20Test extends ApiRunner { @Test void correct() { - List txs = getApi().account().txsERC20("0xE376F69ED2218076682e2b3B7b9099eC50aD68c4"); + List txs = getApi().account().txsErc20("0xE376F69ED2218076682e2b3B7b9099eC50aD68c4"); assertNotNull(txs); assertEquals(3, txs.size()); assertTxs(txs); @@ -33,7 +33,7 @@ void correct() { @Test void correctStartBlock() { - List txs = getApi().account().txsERC20("0x36ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7", 5578167); + List txs = getApi().account().txsErc20("0x36ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7", 5578167); assertNotNull(txs); assertEquals(11, txs.size()); assertTxs(txs); @@ -41,7 +41,7 @@ void correctStartBlock() { @Test void correctStartBlockEndBlock() { - List txs = getApi().account().txsERC20("0x36ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7", 5578167, 5813576); + List txs = getApi().account().txsErc20("0x36ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7", 5578167, 5813576); assertNotNull(txs); assertEquals(5, txs.size()); assertTxs(txs); @@ -50,18 +50,18 @@ void correctStartBlockEndBlock() { @Test void invalidParamWithError() { assertThrows(EtherScanInvalidAddressException.class, - () -> getApi().account().txsERC20("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7")); + () -> getApi().account().txsErc20("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7")); } @Test void correctParamWithEmptyExpectedResult() { - List txs = getApi().account().txsERC20("0x31ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); + List txs = getApi().account().txsErc20("0x31ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); assertNotNull(txs); assertTrue(txs.isEmpty()); } - private void assertTxs(List txs) { - for (TxERC20 tx : txs) { + private void assertTxs(List txs) { + for (TxErc20 tx : txs) { assertNotNull(tx.getBlockHash()); assertNotNull(tx.getTokenName()); assertNotNull(tx.getTokenSymbol()); diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc1155TokenTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc1155TokenTest.java new file mode 100644 index 0000000..ce3a680 --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc1155TokenTest.java @@ -0,0 +1,81 @@ +package io.goodforgod.api.etherscan.account; + +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.model.TxErc1155; +import java.util.List; +import org.junit.jupiter.api.Test; + +/** + * @author GoodforGod + * @since 14.05.2023 + */ +class AccountTxRc1155TokenTest extends ApiRunner { + + @Test + void correct() { + List txs = getApi().account().txsErc1155("0xE4C8324534C0C6bCA174Cd0F02fAC9889C36bA59"); + assertNotNull(txs); + assertFalse(txs.isEmpty()); + assertTxs(txs); + assertNotEquals(0, txs.get(0).getGasPrice()); + assertNotEquals(-1, txs.get(0).getNonce()); + + assertNotNull(txs.get(0).toString()); + assertNotEquals(txs.get(0).toString(), txs.get(1).toString()); + + assertNotEquals(txs.get(0), txs.get(1)); + assertNotEquals(txs.get(0).hashCode(), txs.get(1).hashCode()); + + assertEquals(txs.get(1), txs.get(1)); + assertEquals(txs.get(1).hashCode(), txs.get(1).hashCode()); + } + + @Test + void correctStartBlock() { + List txs = getApi().account().txsErc1155("0xE4C8324534C0C6bCA174Cd0F02fAC9889C36bA59", 14275897); + assertNotNull(txs); + assertFalse(txs.isEmpty()); + assertTxs(txs); + } + + @Test + void correctStartBlockEndBlock() { + List txs = getApi().account().txsErc1155("0xE4C8324534C0C6bCA174Cd0F02fAC9889C36bA59", 14275897, 15148929); + assertNotNull(txs); + assertEquals(11, txs.size()); + assertTxs(txs); + } + + @Test + void invalidParamWithError() { + assertThrows(EtherScanInvalidAddressException.class, + () -> getApi().account().txsErc1155("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7")); + } + + @Test + void correctParamWithEmptyExpectedResult() { + List txs = getApi().account().txsErc1155("0x31ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); + assertNotNull(txs); + assertTrue(txs.isEmpty()); + } + + private void assertTxs(List txs) { + txs.forEach(this::asserTx); + } + + private void asserTx(TxErc1155 tx) { + assertNotNull(tx.getBlockHash()); + assertNotNull(tx.getTokenName()); + assertNotNull(tx.getTokenSymbol()); + assertNotNull(tx.getFrom()); + assertNotNull(tx.getTo()); + assertNotNull(tx.getTimeStamp()); + assertNotNull(tx.getTokenID()); + assertNotNull(tx.getTokenValue()); + assertNotEquals(-1, (tx.getConfirmations())); + assertNotNull(tx.getGasUsed()); + assertNotEquals(-1, tx.getCumulativeGasUsed()); + assertNotEquals(-1, tx.getTransactionIndex()); + } +} diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTest.java index 31c8533..b7988db 100644 --- a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTest.java @@ -2,7 +2,7 @@ import io.goodforgod.api.etherscan.ApiRunner; import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; -import io.goodforgod.api.etherscan.model.TxERC721; +import io.goodforgod.api.etherscan.model.TxErc721; import java.util.List; import org.junit.jupiter.api.Test; @@ -14,7 +14,7 @@ class AccountTxRc721TokenTest extends ApiRunner { @Test void correct() { - List txs = getApi().account().txsERC721("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67"); + List txs = getApi().account().txsErc721("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67"); assertNotNull(txs); assertEquals(16, txs.size()); assertTxs(txs); @@ -33,7 +33,7 @@ void correct() { @Test void correctStartBlock() { - List txs = getApi().account().txsERC721("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67", 4762071); + List txs = getApi().account().txsErc721("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67", 4762071); System.out.println(txs); assertNotNull(txs); assertEquals(5, txs.size()); @@ -42,7 +42,7 @@ void correctStartBlock() { @Test void correctStartBlockEndBlock() { - List txs = getApi().account().txsERC721("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67", 4761862, 4761934); + List txs = getApi().account().txsErc721("0x1a1ebe0d86f72884c3fd484ae1e796e08f8ffa67", 4761862, 4761934); System.out.println(txs); assertNotNull(txs); assertEquals(11, txs.size()); @@ -52,18 +52,18 @@ void correctStartBlockEndBlock() { @Test void invalidParamWithError() { assertThrows(EtherScanInvalidAddressException.class, - () -> getApi().account().txsERC721("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7")); + () -> getApi().account().txsErc721("0x6ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7")); } @Test void correctParamWithEmptyExpectedResult() { - List txs = getApi().account().txsERC721("0x31ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); + List txs = getApi().account().txsErc721("0x31ec53A8fBa6358d59B3C4476D82cc60A2B0FaD7"); assertNotNull(txs); assertTrue(txs.isEmpty()); } - private void assertTxs(List txs) { - for (TxERC721 tx : txs) { + private void assertTxs(List txs) { + for (TxErc721 tx : txs) { assertNotNull(tx.getBlockHash()); assertNotNull(tx.getTokenName()); assertNotNull(tx.getTokenSymbol()); From 34ca1a7f250b7da9a2fa08d0fb3138c3bdc2d812 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 14 May 2023 02:38:15 +0300 Subject: [PATCH 19/61] [2.0.0-SNAPSHOT] EthHttpClient contract and package refactoring --- .../api/etherscan/AccountAPIProvider.java | 7 ++-- .../api/etherscan/BasicProvider.java | 27 +++++++-------- .../api/etherscan/BlockAPIProvider.java | 7 ++-- .../api/etherscan/ContractAPIProvider.java | 7 ++-- .../goodforgod/api/etherscan/Converter.java | 13 ++++++++ .../api/etherscan/EthScanAPIBuilder.java | 24 ++++++++++++-- .../api/etherscan/EtherScanAPI.java | 5 ++- .../api/etherscan/EtherScanAPIProvider.java | 25 +++++++------- .../api/etherscan/GasTrackerAPIProvider.java | 7 ++-- .../api/etherscan/LogsAPIProvider.java | 7 ++-- .../api/etherscan/ProxyAPIProvider.java | 11 ++++--- .../api/etherscan/StatisticAPIProvider.java | 11 ++++--- .../api/etherscan/TransactionAPIProvider.java | 7 ++-- .../{executor => http}/EthHttpClient.java | 17 ++++++---- .../impl/UrlEthHttpClient.java | 32 +++++++++--------- .../api/etherscan/model/TxErc1155.java | 19 +++++++++++ .../api/etherscan/model/TxErc20.java | 19 +++++++++++ .../api/etherscan/model/TxErc721.java | 19 +++++++++++ .../goodforgod/api/etherscan/model/Wei.java | 4 +++ .../api/etherscan/EtherScanAPITests.java | 6 ++-- .../gastracker/GasTrackerApiTest.java | 33 +++++++++++++++++++ 21 files changed, 223 insertions(+), 84 deletions(-) create mode 100644 src/main/java/io/goodforgod/api/etherscan/Converter.java rename src/main/java/io/goodforgod/api/etherscan/{executor => http}/EthHttpClient.java (50%) rename src/main/java/io/goodforgod/api/etherscan/{executor => http}/impl/UrlEthHttpClient.java (85%) create mode 100644 src/test/java/io/goodforgod/api/etherscan/gastracker/GasTrackerApiTest.java diff --git a/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java index 9382618..1b7bce6 100644 --- a/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java @@ -2,7 +2,7 @@ import io.goodforgod.api.etherscan.error.EtherScanException; import io.goodforgod.api.etherscan.error.EtherScanResponseException; -import io.goodforgod.api.etherscan.executor.EthHttpClient; +import io.goodforgod.api.etherscan.http.EthHttpClient; import io.goodforgod.api.etherscan.manager.RequestQueueManager; import io.goodforgod.api.etherscan.model.*; import io.goodforgod.api.etherscan.model.response.*; @@ -49,8 +49,9 @@ final class AccountAPIProvider extends BasicProvider implements AccountAPI { AccountAPIProvider(RequestQueueManager requestQueueManager, String baseUrl, - EthHttpClient executor) { - super(requestQueueManager, "account", baseUrl, executor); + EthHttpClient executor, + Converter converter) { + super(requestQueueManager, "account", baseUrl, executor, converter); } @NotNull diff --git a/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java b/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java index a4ce2a6..f1867d1 100644 --- a/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java @@ -1,15 +1,15 @@ package io.goodforgod.api.etherscan; -import com.google.gson.Gson; import io.goodforgod.api.etherscan.error.EtherScanException; import io.goodforgod.api.etherscan.error.EtherScanParseException; import io.goodforgod.api.etherscan.error.EtherScanRateLimitException; import io.goodforgod.api.etherscan.error.EtherScanResponseException; -import io.goodforgod.api.etherscan.executor.EthHttpClient; +import io.goodforgod.api.etherscan.http.EthHttpClient; import io.goodforgod.api.etherscan.manager.RequestQueueManager; import io.goodforgod.api.etherscan.model.response.StringResponseTO; import io.goodforgod.api.etherscan.util.BasicUtils; -import io.goodforgod.gson.configuration.GsonConfiguration; +import java.net.URI; +import java.nio.charset.StandardCharsets; import java.util.Map; /** @@ -30,22 +30,23 @@ abstract class BasicProvider { private final String baseUrl; private final EthHttpClient executor; private final RequestQueueManager queue; - private final Gson gson; + private final Converter converter; BasicProvider(RequestQueueManager requestQueueManager, String module, String baseUrl, - EthHttpClient ethHttpClient) { + EthHttpClient ethHttpClient, + Converter converter) { this.queue = requestQueueManager; this.module = "&module=" + module; this.baseUrl = baseUrl; this.executor = ethHttpClient; - this.gson = new GsonConfiguration().builder().create(); + this.converter = converter; } T convert(String json, Class tClass) { try { - final T t = gson.fromJson(json, tClass); + final T t = converter.fromJson(json, tClass); if (t instanceof StringResponseTO && ((StringResponseTO) t).getResult().startsWith("Max rate limit reached")) { throw new EtherScanRateLimitException(((StringResponseTO) t).getResult()); } @@ -53,7 +54,7 @@ T convert(String json, Class tClass) { return t; } catch (Exception e) { try { - final Map map = gson.fromJson(json, Map.class); + final Map map = converter.fromJson(json, Map.class); final Object result = map.get("result"); if (result instanceof String && ((String) result).startsWith("Max rate limit reached")) throw new EtherScanRateLimitException(((String) result)); @@ -69,18 +70,18 @@ T convert(String json, Class tClass) { String getRequest(String urlParameters) { queue.takeTurn(); - final String url = baseUrl + module + urlParameters; - final String result = executor.get(url); + final URI uri = URI.create(baseUrl + module + urlParameters); + final String result = executor.get(uri); if (BasicUtils.isEmpty(result)) - throw new EtherScanResponseException("Server returned null value for GET request at URL - " + url); + throw new EtherScanResponseException("Server returned null value for GET request at URL - " + uri); return result; } String postRequest(String urlParameters, String dataToPost) { queue.takeTurn(); - final String url = baseUrl + module + urlParameters; - return executor.post(url, dataToPost); + final URI uri = URI.create(baseUrl + module + urlParameters); + return executor.post(uri, dataToPost.getBytes(StandardCharsets.UTF_8)); } T getRequest(String urlParameters, Class tClass) { diff --git a/src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java index e5a6d49..98a2d90 100644 --- a/src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java @@ -1,7 +1,7 @@ package io.goodforgod.api.etherscan; import io.goodforgod.api.etherscan.error.EtherScanException; -import io.goodforgod.api.etherscan.executor.EthHttpClient; +import io.goodforgod.api.etherscan.http.EthHttpClient; import io.goodforgod.api.etherscan.manager.RequestQueueManager; import io.goodforgod.api.etherscan.model.BlockUncle; import io.goodforgod.api.etherscan.model.response.UncleBlockResponseTO; @@ -24,8 +24,9 @@ final class BlockAPIProvider extends BasicProvider implements BlockAPI { BlockAPIProvider(RequestQueueManager requestQueueManager, String baseUrl, - EthHttpClient executor) { - super(requestQueueManager, "block", baseUrl, executor); + EthHttpClient executor, + Converter converter) { + super(requestQueueManager, "block", baseUrl, executor, converter); } @NotNull diff --git a/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java index cd96f68..1a8fa9a 100644 --- a/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java @@ -2,7 +2,7 @@ import io.goodforgod.api.etherscan.error.EtherScanException; import io.goodforgod.api.etherscan.error.EtherScanResponseException; -import io.goodforgod.api.etherscan.executor.EthHttpClient; +import io.goodforgod.api.etherscan.http.EthHttpClient; import io.goodforgod.api.etherscan.manager.RequestQueueManager; import io.goodforgod.api.etherscan.model.Abi; import io.goodforgod.api.etherscan.model.response.StringResponseTO; @@ -24,8 +24,9 @@ final class ContractAPIProvider extends BasicProvider implements ContractAPI { ContractAPIProvider(RequestQueueManager requestQueueManager, String baseUrl, - EthHttpClient executor) { - super(requestQueueManager, "contract", baseUrl, executor); + EthHttpClient executor, + Converter converter) { + super(requestQueueManager, "contract", baseUrl, executor, converter); } @NotNull diff --git a/src/main/java/io/goodforgod/api/etherscan/Converter.java b/src/main/java/io/goodforgod/api/etherscan/Converter.java new file mode 100644 index 0000000..e8c577a --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/Converter.java @@ -0,0 +1,13 @@ +package io.goodforgod.api.etherscan; + +import org.jetbrains.annotations.NotNull; + +/** + * @author Anton Kurako (GoodforGod) + * @since 14.05.2023 + */ +public interface Converter { + + @NotNull + T fromJson(@NotNull String json, @NotNull Class type); +} diff --git a/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java b/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java index 6571121..501bdb1 100644 --- a/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java +++ b/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java @@ -1,11 +1,13 @@ package io.goodforgod.api.etherscan; +import com.google.gson.Gson; import io.goodforgod.api.etherscan.error.EtherScanKeyException; -import io.goodforgod.api.etherscan.executor.EthHttpClient; -import io.goodforgod.api.etherscan.executor.impl.UrlEthHttpClient; +import io.goodforgod.api.etherscan.http.EthHttpClient; +import io.goodforgod.api.etherscan.http.impl.UrlEthHttpClient; import io.goodforgod.api.etherscan.manager.RequestQueueManager; import io.goodforgod.api.etherscan.manager.impl.FakeRequestQueueManager; import io.goodforgod.api.etherscan.util.BasicUtils; +import io.goodforgod.gson.configuration.GsonConfiguration; import java.util.function.Supplier; import org.jetbrains.annotations.NotNull; @@ -18,10 +20,19 @@ final class EthScanAPIBuilder implements EtherScanAPI.Builder { private static final Supplier DEFAULT_SUPPLIER = UrlEthHttpClient::new; private static final String DEFAULT_KEY = "YourApiKeyToken"; + private final Gson gson = new GsonConfiguration().builder().create(); + private String apiKey = DEFAULT_KEY; private EthNetwork ethNetwork = EthNetworks.MAINNET; private RequestQueueManager queueManager = RequestQueueManager.DEFAULT; private Supplier ethHttpClientSupplier = DEFAULT_SUPPLIER; + private Supplier converterSupplier = () -> new Converter() { + + @Override + public @NotNull T fromJson(@NotNull String json, @NotNull Class type) { + return gson.fromJson(json, type); + } + }; @NotNull @Override @@ -64,8 +75,15 @@ public EtherScanAPI.Builder withHttpClient(@NotNull Supplier http return this; } + @NotNull + @Override + public EtherScanAPI.Builder withConverter(@NotNull Supplier converterSupplier) { + this.converterSupplier = converterSupplier; + return this; + } + @Override public @NotNull EtherScanAPI build() { - return new EtherScanAPIProvider(apiKey, ethNetwork, ethHttpClientSupplier, queueManager); + return new EtherScanAPIProvider(apiKey, ethNetwork, queueManager, ethHttpClientSupplier.get(), converterSupplier.get()); } } diff --git a/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java index d902074..6da3d8f 100644 --- a/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java +++ b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java @@ -1,6 +1,6 @@ package io.goodforgod.api.etherscan; -import io.goodforgod.api.etherscan.executor.EthHttpClient; +import io.goodforgod.api.etherscan.http.EthHttpClient; import io.goodforgod.api.etherscan.manager.RequestQueueManager; import java.util.function.Supplier; import org.jetbrains.annotations.NotNull; @@ -59,6 +59,9 @@ interface Builder { @NotNull Builder withHttpClient(@NotNull Supplier httpClientSupplier); + @NotNull + Builder withConverter(@NotNull Supplier converterSupplier); + @NotNull EtherScanAPI build(); } diff --git a/src/main/java/io/goodforgod/api/etherscan/EtherScanAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPIProvider.java index 675836f..e698f45 100644 --- a/src/main/java/io/goodforgod/api/etherscan/EtherScanAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPIProvider.java @@ -1,8 +1,7 @@ package io.goodforgod.api.etherscan; -import io.goodforgod.api.etherscan.executor.EthHttpClient; +import io.goodforgod.api.etherscan.http.EthHttpClient; import io.goodforgod.api.etherscan.manager.RequestQueueManager; -import java.util.function.Supplier; import org.jetbrains.annotations.NotNull; /** @@ -25,21 +24,21 @@ final class EtherScanAPIProvider implements EtherScanAPI { EtherScanAPIProvider(String apiKey, EthNetwork network, - Supplier executorSupplier, - RequestQueueManager queue) { + RequestQueueManager queue, + EthHttpClient ethHttpClient, + Converter converter) { // EtherScan 1request\5sec limit support by queue manager - final EthHttpClient ethHttpClient = executorSupplier.get(); final String baseUrl = network.domain() + "?apikey=" + apiKey; this.requestQueueManager = queue; - this.account = new AccountAPIProvider(queue, baseUrl, ethHttpClient); - this.block = new BlockAPIProvider(queue, baseUrl, ethHttpClient); - this.contract = new ContractAPIProvider(queue, baseUrl, ethHttpClient); - this.logs = new LogsAPIProvider(queue, baseUrl, ethHttpClient); - this.proxy = new ProxyAPIProvider(queue, baseUrl, ethHttpClient); - this.stats = new StatisticAPIProvider(queue, baseUrl, ethHttpClient); - this.txs = new TransactionAPIProvider(queue, baseUrl, ethHttpClient); - this.gasTracker = new GasTrackerAPIProvider(queue, baseUrl, ethHttpClient); + this.account = new AccountAPIProvider(queue, baseUrl, ethHttpClient, converter); + this.block = new BlockAPIProvider(queue, baseUrl, ethHttpClient, converter); + this.contract = new ContractAPIProvider(queue, baseUrl, ethHttpClient, converter); + this.logs = new LogsAPIProvider(queue, baseUrl, ethHttpClient, converter); + this.proxy = new ProxyAPIProvider(queue, baseUrl, ethHttpClient, converter); + this.stats = new StatisticAPIProvider(queue, baseUrl, ethHttpClient, converter); + this.txs = new TransactionAPIProvider(queue, baseUrl, ethHttpClient, converter); + this.gasTracker = new GasTrackerAPIProvider(queue, baseUrl, ethHttpClient, converter); } @NotNull diff --git a/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java index d4ec276..a4db5ae 100644 --- a/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java @@ -2,7 +2,7 @@ import io.goodforgod.api.etherscan.error.EtherScanException; import io.goodforgod.api.etherscan.error.EtherScanResponseException; -import io.goodforgod.api.etherscan.executor.EthHttpClient; +import io.goodforgod.api.etherscan.http.EthHttpClient; import io.goodforgod.api.etherscan.manager.RequestQueueManager; import io.goodforgod.api.etherscan.model.GasEstimate; import io.goodforgod.api.etherscan.model.GasOracle; @@ -27,8 +27,9 @@ final class GasTrackerAPIProvider extends BasicProvider implements GasTrackerAPI GasTrackerAPIProvider(RequestQueueManager queue, String baseUrl, - EthHttpClient ethHttpClient) { - super(queue, "gastracker", baseUrl, ethHttpClient); + EthHttpClient ethHttpClient, + Converter converter) { + super(queue, "gastracker", baseUrl, ethHttpClient, converter); } @Override diff --git a/src/main/java/io/goodforgod/api/etherscan/LogsAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/LogsAPIProvider.java index 771d931..fe9d420 100644 --- a/src/main/java/io/goodforgod/api/etherscan/LogsAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/LogsAPIProvider.java @@ -1,7 +1,7 @@ package io.goodforgod.api.etherscan; import io.goodforgod.api.etherscan.error.EtherScanException; -import io.goodforgod.api.etherscan.executor.EthHttpClient; +import io.goodforgod.api.etherscan.http.EthHttpClient; import io.goodforgod.api.etherscan.manager.RequestQueueManager; import io.goodforgod.api.etherscan.model.Log; import io.goodforgod.api.etherscan.model.query.LogQuery; @@ -24,8 +24,9 @@ final class LogsAPIProvider extends BasicProvider implements LogsAPI { LogsAPIProvider(RequestQueueManager queue, String baseUrl, - EthHttpClient executor) { - super(queue, "logs", baseUrl, executor); + EthHttpClient executor, + Converter converter) { + super(queue, "logs", baseUrl, executor, converter); } @NotNull diff --git a/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java index 1239294..a306541 100644 --- a/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java @@ -3,7 +3,7 @@ import io.goodforgod.api.etherscan.error.EtherScanException; import io.goodforgod.api.etherscan.error.EtherScanInvalidDataHexException; import io.goodforgod.api.etherscan.error.EtherScanResponseException; -import io.goodforgod.api.etherscan.executor.EthHttpClient; +import io.goodforgod.api.etherscan.http.EthHttpClient; import io.goodforgod.api.etherscan.manager.RequestQueueManager; import io.goodforgod.api.etherscan.model.proxy.BlockProxy; import io.goodforgod.api.etherscan.model.proxy.ReceiptProxy; @@ -56,10 +56,11 @@ final class ProxyAPIProvider extends BasicProvider implements ProxyAPI { private static final Pattern EMPTY_HEX = Pattern.compile("0x0+"); - ProxyAPIProvider(final RequestQueueManager queue, - final String baseUrl, - final EthHttpClient executor) { - super(queue, "proxy", baseUrl, executor); + ProxyAPIProvider(RequestQueueManager queue, + String baseUrl, + EthHttpClient executor, + Converter converter) { + super(queue, "proxy", baseUrl, executor, converter); } @Override diff --git a/src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java index ee4bdaa..1d1bcee 100644 --- a/src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java @@ -2,7 +2,7 @@ import io.goodforgod.api.etherscan.error.EtherScanException; import io.goodforgod.api.etherscan.error.EtherScanResponseException; -import io.goodforgod.api.etherscan.executor.EthHttpClient; +import io.goodforgod.api.etherscan.http.EthHttpClient; import io.goodforgod.api.etherscan.manager.RequestQueueManager; import io.goodforgod.api.etherscan.model.Price; import io.goodforgod.api.etherscan.model.Supply; @@ -27,10 +27,11 @@ final class StatisticAPIProvider extends BasicProvider implements StatisticAPI { private static final String CONTRACT_ADDRESS_PARAM = "&contractaddress="; - StatisticAPIProvider(final RequestQueueManager queue, - final String baseUrl, - final EthHttpClient executor) { - super(queue, "stats", baseUrl, executor); + StatisticAPIProvider(RequestQueueManager queue, + String baseUrl, + EthHttpClient executor, + Converter converter) { + super(queue, "stats", baseUrl, executor, converter); } @NotNull diff --git a/src/main/java/io/goodforgod/api/etherscan/TransactionAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/TransactionAPIProvider.java index 91082a8..c131079 100644 --- a/src/main/java/io/goodforgod/api/etherscan/TransactionAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/TransactionAPIProvider.java @@ -1,7 +1,7 @@ package io.goodforgod.api.etherscan; import io.goodforgod.api.etherscan.error.EtherScanException; -import io.goodforgod.api.etherscan.executor.EthHttpClient; +import io.goodforgod.api.etherscan.http.EthHttpClient; import io.goodforgod.api.etherscan.manager.RequestQueueManager; import io.goodforgod.api.etherscan.model.Status; import io.goodforgod.api.etherscan.model.response.ReceiptStatusResponseTO; @@ -26,8 +26,9 @@ final class TransactionAPIProvider extends BasicProvider implements TransactionA TransactionAPIProvider(RequestQueueManager queue, String baseUrl, - EthHttpClient executor) { - super(queue, "transaction", baseUrl, executor); + EthHttpClient executor, + Converter converter) { + super(queue, "transaction", baseUrl, executor, converter); } @NotNull diff --git a/src/main/java/io/goodforgod/api/etherscan/executor/EthHttpClient.java b/src/main/java/io/goodforgod/api/etherscan/http/EthHttpClient.java similarity index 50% rename from src/main/java/io/goodforgod/api/etherscan/executor/EthHttpClient.java rename to src/main/java/io/goodforgod/api/etherscan/http/EthHttpClient.java index 4edc507..f4b559d 100644 --- a/src/main/java/io/goodforgod/api/etherscan/executor/EthHttpClient.java +++ b/src/main/java/io/goodforgod/api/etherscan/http/EthHttpClient.java @@ -1,4 +1,7 @@ -package io.goodforgod.api.etherscan.executor; +package io.goodforgod.api.etherscan.http; + +import java.net.URI; +import org.jetbrains.annotations.NotNull; /** * Http Client interface @@ -11,17 +14,19 @@ public interface EthHttpClient { /** * Performs a Http GET request * - * @param url as string + * @param uri as string * @return result as string */ - String get(String url); + @NotNull + String get(@NotNull URI uri); /** * Performs a Http POST request * - * @param url as string - * @param data to post + * @param uri as string + * @param body to post * @return result as string */ - String post(String url, String data); + @NotNull + String post(@NotNull URI uri, byte[] body); } diff --git a/src/main/java/io/goodforgod/api/etherscan/executor/impl/UrlEthHttpClient.java b/src/main/java/io/goodforgod/api/etherscan/http/impl/UrlEthHttpClient.java similarity index 85% rename from src/main/java/io/goodforgod/api/etherscan/executor/impl/UrlEthHttpClient.java rename to src/main/java/io/goodforgod/api/etherscan/http/impl/UrlEthHttpClient.java index ac05125..4178be7 100644 --- a/src/main/java/io/goodforgod/api/etherscan/executor/impl/UrlEthHttpClient.java +++ b/src/main/java/io/goodforgod/api/etherscan/http/impl/UrlEthHttpClient.java @@ -1,17 +1,17 @@ -package io.goodforgod.api.etherscan.executor.impl; +package io.goodforgod.api.etherscan.http.impl; import static java.net.HttpURLConnection.*; import io.goodforgod.api.etherscan.error.EtherScanConnectionException; import io.goodforgod.api.etherscan.error.EtherScanTimeoutException; -import io.goodforgod.api.etherscan.executor.EthHttpClient; -import io.goodforgod.api.etherscan.util.BasicUtils; +import io.goodforgod.api.etherscan.http.EthHttpClient; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.SocketTimeoutException; +import java.net.URI; import java.net.URL; import java.nio.charset.StandardCharsets; import java.time.Duration; @@ -71,8 +71,8 @@ public UrlEthHttpClient(Duration connectTimeout, this.headers = Collections.unmodifiableMap(headers); } - private HttpURLConnection buildConnection(String urlAsString, String method) throws IOException { - final URL url = new URL(urlAsString); + private HttpURLConnection buildConnection(URI uri, String method) throws IOException { + final URL url = uri.toURL(); final HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod(method); connection.setConnectTimeout(connectTimeout); @@ -82,12 +82,12 @@ private HttpURLConnection buildConnection(String urlAsString, String method) thr } @Override - public String get(final String urlAsString) { + public String get(URI uri) { try { - final HttpURLConnection connection = buildConnection(urlAsString, "GET"); + final HttpURLConnection connection = buildConnection(uri, "GET"); final int status = connection.getResponseCode(); if (status == HTTP_MOVED_TEMP || status == HTTP_MOVED_PERM) { - return get(connection.getHeaderField("Location")); + return get(URI.create(connection.getHeaderField("Location"))); } else if ((status >= HTTP_BAD_REQUEST) && (status < HTTP_INTERNAL_ERROR)) { throw new EtherScanConnectionException("Protocol error: " + connection.getResponseMessage()); } else if (status >= HTTP_INTERNAL_ERROR) { @@ -105,25 +105,23 @@ public String get(final String urlAsString) { } @Override - public String post(String urlAsString, String dataToPost) { + public String post(URI uri, byte[] body) { try { - final HttpURLConnection connection = buildConnection(urlAsString, "POST"); - final String contentLength = (BasicUtils.isBlank(dataToPost)) - ? "0" - : String.valueOf(dataToPost.length()); + final HttpURLConnection connection = buildConnection(uri, "POST"); + final int contentLength = body.length; connection.setRequestProperty("Content-Type", "application/json; charset=UTF-8"); - connection.setRequestProperty("Content-Length", contentLength); - connection.setFixedLengthStreamingMode(dataToPost.length()); + connection.setRequestProperty("Content-Length", String.valueOf(contentLength)); + connection.setFixedLengthStreamingMode(body.length); connection.setDoOutput(true); connection.connect(); try (OutputStream os = connection.getOutputStream()) { - os.write(dataToPost.getBytes(StandardCharsets.UTF_8)); + os.write(body); } final int status = connection.getResponseCode(); if (status == HTTP_MOVED_TEMP || status == HTTP_MOVED_PERM) { - return post(connection.getHeaderField("Location"), dataToPost); + return post(URI.create(connection.getHeaderField("Location")), body); } else if ((status >= HTTP_BAD_REQUEST) && (status < HTTP_INTERNAL_ERROR)) { throw new EtherScanConnectionException("Protocol error: " + connection.getResponseMessage()); } else if (status >= HTTP_INTERNAL_ERROR) { diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java b/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java index e57af5f..84e2d40 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java @@ -3,6 +3,7 @@ import java.math.BigInteger; import java.time.LocalDateTime; import java.time.ZoneOffset; +import java.util.Objects; /** * @author GoodforGod @@ -63,6 +64,24 @@ public long getConfirmations() { } // + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof TxErc1155)) + return false; + if (!super.equals(o)) + return false; + TxErc1155 txErc1155 = (TxErc1155) o; + return Objects.equals(tokenID, txErc1155.tokenID) && Objects.equals(tokenName, txErc1155.tokenName) + && Objects.equals(tokenSymbol, txErc1155.tokenSymbol) && Objects.equals(tokenValue, txErc1155.tokenValue); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), tokenID, tokenName, tokenSymbol, tokenValue); + } + @Override public String toString() { return "TxERC721{" + diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java b/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java index da6f54f..f51b855 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java @@ -3,6 +3,7 @@ import java.math.BigInteger; import java.time.LocalDateTime; import java.time.ZoneOffset; +import java.util.Objects; /** * @author GoodforGod @@ -63,6 +64,24 @@ public long getConfirmations() { } // + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof TxErc20)) + return false; + if (!super.equals(o)) + return false; + TxErc20 txErc20 = (TxErc20) o; + return Objects.equals(tokenName, txErc20.tokenName) && Objects.equals(tokenSymbol, txErc20.tokenSymbol) + && Objects.equals(tokenDecimal, txErc20.tokenDecimal); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), tokenName, tokenSymbol, tokenDecimal); + } + @Override public String toString() { return "TxERC20{" + diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java b/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java index 548113c..8fb2467 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java @@ -3,6 +3,7 @@ import java.math.BigInteger; import java.time.LocalDateTime; import java.time.ZoneOffset; +import java.util.Objects; /** * @author GoodforGod @@ -63,6 +64,24 @@ public long getConfirmations() { } // + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof TxErc721)) + return false; + if (!super.equals(o)) + return false; + TxErc721 txErc721 = (TxErc721) o; + return Objects.equals(tokenID, txErc721.tokenID) && Objects.equals(tokenName, txErc721.tokenName) + && Objects.equals(tokenSymbol, txErc721.tokenSymbol) && Objects.equals(tokenDecimal, txErc721.tokenDecimal); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), tokenID, tokenName, tokenSymbol, tokenDecimal); + } + @Override public String toString() { return "TxERC721{" + diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Wei.java b/src/main/java/io/goodforgod/api/etherscan/model/Wei.java index 85dd3ab..e863b7a 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Wei.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Wei.java @@ -11,6 +11,10 @@ public class Wei { private final BigInteger result; + public Wei(long value) { + this.result = BigInteger.valueOf(value); + } + public Wei(BigInteger value) { this.result = value; } diff --git a/src/test/java/io/goodforgod/api/etherscan/EtherScanAPITests.java b/src/test/java/io/goodforgod/api/etherscan/EtherScanAPITests.java index 5cc0fe7..26865f5 100644 --- a/src/test/java/io/goodforgod/api/etherscan/EtherScanAPITests.java +++ b/src/test/java/io/goodforgod/api/etherscan/EtherScanAPITests.java @@ -2,8 +2,8 @@ import io.goodforgod.api.etherscan.error.EtherScanKeyException; import io.goodforgod.api.etherscan.error.EtherScanTimeoutException; -import io.goodforgod.api.etherscan.executor.EthHttpClient; -import io.goodforgod.api.etherscan.executor.impl.UrlEthHttpClient; +import io.goodforgod.api.etherscan.http.EthHttpClient; +import io.goodforgod.api.etherscan.http.impl.UrlEthHttpClient; import io.goodforgod.api.etherscan.model.Balance; import java.time.Duration; import java.util.concurrent.TimeUnit; @@ -17,10 +17,10 @@ class EtherScanAPITests extends ApiRunner { private final EthNetworks network = EthNetworks.KOVAN; - private final String validKey = "YourKey"; @Test void validKey() { + String validKey = "YourKey"; EtherScanAPI api = EtherScanAPI.builder().withApiKey(validKey).withNetwork(network).build(); assertNotNull(api); } diff --git a/src/test/java/io/goodforgod/api/etherscan/gastracker/GasTrackerApiTest.java b/src/test/java/io/goodforgod/api/etherscan/gastracker/GasTrackerApiTest.java new file mode 100644 index 0000000..ec904a6 --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/gastracker/GasTrackerApiTest.java @@ -0,0 +1,33 @@ +package io.goodforgod.api.etherscan.gastracker; + +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.model.GasEstimate; +import io.goodforgod.api.etherscan.model.GasOracle; +import io.goodforgod.api.etherscan.model.Wei; +import org.junit.jupiter.api.Test; + +/** + * @author GoodforGod + * @since 14.05.2023 + */ +class GasTrackerApiTest extends ApiRunner { + + @Test + void estimate() { + GasEstimate estimate = getApi().gasTracker().estimate(new Wei(123)); + assertNotNull(estimate); + assertNotNull(estimate.getDuration()); + } + + @Test + void oracle() { + GasOracle oracle = getApi().gasTracker().oracle(); + assertNotNull(oracle); + assertNotNull(oracle.getGasUsedRatio()); + assertNotNull(oracle.getFastGasPriceInWei()); + assertNotNull(oracle.getLastBlock()); + assertNotNull(oracle.getProposeGasPriceInWei()); + assertNotNull(oracle.getSafeGasPriceInWei()); + assertNotNull(oracle.getSuggestBaseFee()); + } +} From f095f0fa4778f5edac65a9f5558812ff4bfd9c9c Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 14 May 2023 02:41:55 +0300 Subject: [PATCH 20/61] [2.0.0-SNAPSHOT] Javadoc improved --- .../api/etherscan/model/query/LogQuery.java | 12 ++++++------ .../etherscan/model/query/LogQueryBuilderImpl.java | 9 +++++---- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/goodforgod/api/etherscan/model/query/LogQuery.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQuery.java index 69e8409..9d8ea5a 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/query/LogQuery.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQuery.java @@ -7,7 +7,7 @@ import org.jetbrains.annotations.NotNull; /** - * Final builded container for The Event Log API + * Final built container for The Event Log API * EtherScan - API Descriptions ... * * @see LogQueryBuilderImpl @@ -21,7 +21,7 @@ public interface LogQuery { String params(); @NotNull - static Builder builder(String address) { + static Builder builder(@NotNull String address) { return new LogQueryBuilderImpl(address, MIN_BLOCK, MAX_BLOCK); } @@ -34,16 +34,16 @@ interface Builder { LogQuery.Builder withBlockTo(long endBlock); @NotNull - LogTopicSingle withTopic(String topic0); + LogTopicSingle withTopic(@NotNull String topic0); @NotNull - LogTopicTuple withTopic(String topic0, String topic1); + LogTopicTuple withTopic(@NotNull String topic0, @NotNull String topic1); @NotNull - LogTopicTriple withTopic(String topic0, String topic1, String topic2); + LogTopicTriple withTopic(@NotNull String topic0, @NotNull String topic1, @NotNull String topic2); @NotNull - LogTopicQuadro withTopic(String topic0, String topic1, String topic2, String topic3); + LogTopicQuadro withTopic(@NotNull String topic0, @NotNull String topic1, @NotNull String topic2, @NotNull String topic3); @NotNull LogQuery build(); diff --git a/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryBuilderImpl.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryBuilderImpl.java index 0d1eb59..750ab49 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryBuilderImpl.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryBuilderImpl.java @@ -37,14 +37,14 @@ final class LogQueryBuilderImpl implements LogQuery.Builder { } @Override - public @NotNull LogTopicSingle withTopic(String topic0) { + public @NotNull LogTopicSingle withTopic(@NotNull String topic0) { if (BasicUtils.isNotHex(topic0)) throw new ErtherScanLogQueryException("topic0 can not be empty or non hex."); return new LogTopicSingle(address, startBlock, endBlock, topic0); } @Override - public @NotNull LogTopicTuple withTopic(String topic0, String topic1) { + public @NotNull LogTopicTuple withTopic(@NotNull String topic0, @NotNull String topic1) { if (BasicUtils.isNotHex(topic0)) throw new ErtherScanLogQueryException("topic0 can not be empty or non hex."); if (BasicUtils.isNotHex(topic1)) @@ -53,7 +53,7 @@ final class LogQueryBuilderImpl implements LogQuery.Builder { } @Override - public @NotNull LogTopicTriple withTopic(String topic0, String topic1, String topic2) { + public @NotNull LogTopicTriple withTopic(@NotNull String topic0, @NotNull String topic1, @NotNull String topic2) { if (BasicUtils.isNotHex(topic0)) throw new ErtherScanLogQueryException("topic0 can not be empty or non hex."); if (BasicUtils.isNotHex(topic1)) @@ -64,7 +64,8 @@ final class LogQueryBuilderImpl implements LogQuery.Builder { } @Override - public @NotNull LogTopicQuadro withTopic(String topic0, String topic1, String topic2, String topic3) { + public @NotNull LogTopicQuadro + withTopic(@NotNull String topic0, @NotNull String topic1, @NotNull String topic2, @NotNull String topic3) { if (BasicUtils.isNotHex(topic0)) throw new ErtherScanLogQueryException("topic0 can not be empty or non hex."); if (BasicUtils.isNotHex(topic1)) From 70cee44655da6e6a7c42e11007eb3f7ef8cb1153 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 14 May 2023 02:50:02 +0300 Subject: [PATCH 21/61] [2.0.0-SNAPSHOT] LogQueryBuilderImpl address validation added --- .../etherscan/manager/impl/SemaphoreRequestQueueManager.java | 4 ++-- .../api/etherscan/model/query/LogQueryBuilderImpl.java | 1 + .../java/io/goodforgod/api/etherscan/EtherScanAPITests.java | 4 ++-- .../java/io/goodforgod/api/etherscan/logs/LogsApiTest.java | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/io/goodforgod/api/etherscan/manager/impl/SemaphoreRequestQueueManager.java b/src/main/java/io/goodforgod/api/etherscan/manager/impl/SemaphoreRequestQueueManager.java index ff12cd1..a6e3037 100644 --- a/src/main/java/io/goodforgod/api/etherscan/manager/impl/SemaphoreRequestQueueManager.java +++ b/src/main/java/io/goodforgod/api/etherscan/manager/impl/SemaphoreRequestQueueManager.java @@ -31,8 +31,8 @@ public SemaphoreRequestQueueManager(int size, Duration resetIn, Duration delayIn public SemaphoreRequestQueueManager(int size, Duration queueResetTimeIn, Duration delayIn, int initialSize) { this.semaphore = new Semaphore(initialSize); this.queueResetTimeInMillis = queueResetTimeIn.toMillis(); - this.executorService.scheduleAtFixedRate(releaseLocks(size + 1), delayIn.toMillis(), queueResetTimeInMillis, - TimeUnit.MILLISECONDS); + this.executorService.scheduleAtFixedRate(releaseLocks(size + 1), + delayIn.toMillis(), queueResetTimeInMillis, TimeUnit.MILLISECONDS); } @SuppressWarnings("java:S899") diff --git a/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryBuilderImpl.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryBuilderImpl.java index 750ab49..716cfa4 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryBuilderImpl.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryBuilderImpl.java @@ -21,6 +21,7 @@ final class LogQueryBuilderImpl implements LogQuery.Builder { private final long startBlock, endBlock; LogQueryBuilderImpl(String address, long startBlock, long endBlock) { + BasicUtils.validateAddress(address); this.address = address; this.startBlock = startBlock; this.endBlock = endBlock; diff --git a/src/test/java/io/goodforgod/api/etherscan/EtherScanAPITests.java b/src/test/java/io/goodforgod/api/etherscan/EtherScanAPITests.java index 26865f5..22c58d6 100644 --- a/src/test/java/io/goodforgod/api/etherscan/EtherScanAPITests.java +++ b/src/test/java/io/goodforgod/api/etherscan/EtherScanAPITests.java @@ -1,7 +1,7 @@ package io.goodforgod.api.etherscan; +import io.goodforgod.api.etherscan.error.EtherScanConnectionException; import io.goodforgod.api.etherscan.error.EtherScanKeyException; -import io.goodforgod.api.etherscan.error.EtherScanTimeoutException; import io.goodforgod.api.etherscan.http.EthHttpClient; import io.goodforgod.api.etherscan.http.impl.UrlEthHttpClient; import io.goodforgod.api.etherscan.model.Balance; @@ -70,7 +70,7 @@ void timeout() throws InterruptedException { Supplier supplier = () -> new UrlEthHttpClient(Duration.ofMillis(300), Duration.ofMillis(300)); EtherScanAPI api = EtherScanAPI.builder().withApiKey(getApiKey()).withNetwork(EthNetworks.KOVAN).withHttpClient(supplier) .build(); - assertThrows(EtherScanTimeoutException.class, + assertThrows(EtherScanConnectionException.class, () -> api.account().blocksMined("0x0010f94b296A852aAac52EA6c5Ac72e03afD032D")); } } diff --git a/src/test/java/io/goodforgod/api/etherscan/logs/LogsApiTest.java b/src/test/java/io/goodforgod/api/etherscan/logs/LogsApiTest.java index 0f90d37..7d9fe64 100644 --- a/src/test/java/io/goodforgod/api/etherscan/logs/LogsApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/logs/LogsApiTest.java @@ -38,10 +38,10 @@ static Stream source() { .build(); return Stream.of( - Arguments.of(single, 423), + Arguments.of(single, 424), Arguments.of(singleInvalidAddr, 0), Arguments.of(tupleAnd, 1), - Arguments.of(tupleOr, 425)); + Arguments.of(tupleOr, 426)); } @ParameterizedTest From 43a0693922d071aed0dd46d741854755873b6bfd Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 14 May 2023 02:57:19 +0300 Subject: [PATCH 22/61] [2.0.0-SNAPSHOT] CI Report only for Java 17 (avoid rate limiter) --- .github/workflows/gradle.yml | 1 + src/test/java/io/goodforgod/api/etherscan/ApiRunner.java | 9 ++------- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index e4c7620..b4c0bb4 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -37,6 +37,7 @@ jobs: API_KEY: ${{ secrets.API_KEY }} - name: SonarQube + if: matrix.java == '17' run: ./gradlew sonarqube env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java b/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java index cd4a657..cd8c7d1 100644 --- a/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java +++ b/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java @@ -1,8 +1,6 @@ package io.goodforgod.api.etherscan; import io.goodforgod.api.etherscan.manager.RequestQueueManager; -import io.goodforgod.api.etherscan.manager.impl.SemaphoreRequestQueueManager; -import java.time.Duration; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; @@ -18,14 +16,11 @@ public class ApiRunner extends Assertions { static { final String key = System.getenv("API_KEY"); + final RequestQueueManager queueManager = RequestQueueManager.DEFAULT; + apiKey = (key == null || key.isEmpty()) ? DEFAULT_KEY : key; - - final RequestQueueManager queueManager = (DEFAULT_KEY.equals(apiKey)) - ? RequestQueueManager.DEFAULT - : new SemaphoreRequestQueueManager(1, Duration.ofMillis(1200L), Duration.ofMillis(1200L), 0); - api = EtherScanAPI.builder().withApiKey(ApiRunner.apiKey).withNetwork(EthNetworks.MAINNET).withQueue(queueManager) .build(); apiKovan = EtherScanAPI.builder().withApiKey(ApiRunner.apiKey).withNetwork(EthNetworks.KOVAN).withQueue(queueManager) From 9b366c3b58dc6470b841a9b2b91c21291f2ebc48 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 14 May 2023 03:07:14 +0300 Subject: [PATCH 23/61] [2.0.0-SNAPSHOT] EthNetworks cleanup --- .../goodforgod/api/etherscan/EthNetworks.java | 5 +--- .../goodforgod/api/etherscan/ApiRunner.java | 24 ------------------- 2 files changed, 1 insertion(+), 28 deletions(-) diff --git a/src/main/java/io/goodforgod/api/etherscan/EthNetworks.java b/src/main/java/io/goodforgod/api/etherscan/EthNetworks.java index 4dbe138..9e18508 100644 --- a/src/main/java/io/goodforgod/api/etherscan/EthNetworks.java +++ b/src/main/java/io/goodforgod/api/etherscan/EthNetworks.java @@ -10,11 +10,8 @@ public enum EthNetworks implements EthNetwork { MAINNET("api", "io"), - ROPSTEN("api-ropsten", "io"), - KOVAN("api-kovan", "io"), - TOBALABA("api-tobalaba", "com"), GORLI("api-goerli", "io"), - RINKEBY("api-rinkeby", "io"); + SEPOLIA("api-sepolia", "io"); private final URI domain; diff --git a/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java b/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java index cd8c7d1..7a5ef52 100644 --- a/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java +++ b/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java @@ -9,9 +9,6 @@ public class ApiRunner extends Assertions { private static final String DEFAULT_KEY = "YourApiKeyToken"; private static final EtherScanAPI api; - private static final EtherScanAPI apiRopsten; - private static final EtherScanAPI apiRinkeby; - private static final EtherScanAPI apiKovan; private static final String apiKey; static { @@ -23,12 +20,6 @@ public class ApiRunner extends Assertions { : key; api = EtherScanAPI.builder().withApiKey(ApiRunner.apiKey).withNetwork(EthNetworks.MAINNET).withQueue(queueManager) .build(); - apiKovan = EtherScanAPI.builder().withApiKey(ApiRunner.apiKey).withNetwork(EthNetworks.KOVAN).withQueue(queueManager) - .build(); - apiRopsten = EtherScanAPI.builder().withApiKey(ApiRunner.apiKey).withNetwork(EthNetworks.ROPSTEN).withQueue(queueManager) - .build(); - apiRinkeby = EtherScanAPI.builder().withApiKey(ApiRunner.apiKey).withNetwork(EthNetworks.RINKEBY).withQueue(queueManager) - .build(); } public static String getApiKey() { @@ -39,23 +30,8 @@ public static EtherScanAPI getApi() { return api; } - public static EtherScanAPI getApiRopsten() { - return apiRopsten; - } - - public static EtherScanAPI getApiRinkeby() { - return apiRinkeby; - } - - public static EtherScanAPI getApiKovan() { - return apiKovan; - } - @AfterAll public static void cleanup() throws Exception { api.close(); - apiRopsten.close(); - apiRinkeby.close(); - apiKovan.close(); } } From bf9f02bad2328a37859fe52f4e58b95b210c0d76 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 14 May 2023 03:29:43 +0300 Subject: [PATCH 24/61] [2.0.0-SNAPSHOT] SemaphoreRequestQueueManager improved --- .github/workflows/gradle.yml | 9 +++++- .../manager/RequestQueueManager.java | 7 ++-- .../impl/SemaphoreRequestQueueManager.java | 24 +++++++------- .../goodforgod/api/etherscan/ApiRunner.java | 32 ++++++++++++------- .../api/etherscan/EtherScanAPITests.java | 11 ++++--- ...Test.java => AccountBalanceListTests.java} | 2 +- ...anceTest.java => AccountBalanceTests.java} | 2 +- ...Test.java => AccountMinedBlocksTests.java} | 2 +- ...est.java => AccountTokenBalanceTests.java} | 2 +- ...rc20Test.java => AccountTxErc20Tests.java} | 2 +- ...java => AccountTxInternalByHashTests.java} | 2 +- ...lTest.java => AccountTxInternalTests.java} | 2 +- ...st.java => AccountTxRc1155TokenTests.java} | 2 +- ...est.java => AccountTxRc721TokenTests.java} | 2 +- ...countTxsTest.java => AccountTxsTests.java} | 2 +- .../{BlockApiTest.java => BlockApiTests.java} | 2 +- ...ractApiTest.java => ContractApiTests.java} | 2 +- ...erApiTest.java => GasTrackerApiTests.java} | 2 +- ...derTest.java => LogQueryBuilderTests.java} | 2 +- .../{LogsApiTest.java => LogsApiTests.java} | 2 +- ...=> SemaphoreRequestQueueManagerTests.java} | 15 ++------- .../etherscan/model/ModelBuilderTests.java | 7 ++++ ...ckApiTest.java => ProxyBlockApiTests.java} | 4 +-- ...est.java => ProxyBlockLastNoApiTests.java} | 2 +- ...Test.java => ProxyBlockUncleApiTests.java} | 2 +- ...allApiTest.java => ProxyCallApiTests.java} | 2 +- ...odeApiTest.java => ProxyCodeApiTests.java} | 2 +- ...yGasApiTest.java => ProxyGasApiTests.java} | 2 +- ...ApiTest.java => ProxyStorageApiTests.java} | 2 +- ...oxyTxApiTest.java => ProxyTxApiTests.java} | 2 +- ...ApiTest.java => ProxyTxCountApiTests.java} | 2 +- ...iTest.java => ProxyTxReceiptApiTests.java} | 2 +- ...iTest.java => ProxyTxSendRawApiTests.java} | 2 +- ...iTest.java => StatisticPriceApiTests.java} | 2 +- ...Test.java => StatisticSupplyApiTests.java} | 2 +- ...java => StatisticTokenSupplyApiTests.java} | 2 +- ...Test.java => TransactionExecApiTests.java} | 2 +- ...t.java => TransactionReceiptApiTests.java} | 2 +- 38 files changed, 89 insertions(+), 80 deletions(-) rename src/test/java/io/goodforgod/api/etherscan/account/{AccountBalanceListTest.java => AccountBalanceListTests.java} (98%) rename src/test/java/io/goodforgod/api/etherscan/account/{AccountBalanceTest.java => AccountBalanceTests.java} (96%) rename src/test/java/io/goodforgod/api/etherscan/account/{AccountMinedBlocksTest.java => AccountMinedBlocksTests.java} (96%) rename src/test/java/io/goodforgod/api/etherscan/account/{AccountTokenBalanceTest.java => AccountTokenBalanceTests.java} (97%) rename src/test/java/io/goodforgod/api/etherscan/account/{AccountTxErc20Test.java => AccountTxErc20Tests.java} (98%) rename src/test/java/io/goodforgod/api/etherscan/account/{AccountTxInternalByHashTest.java => AccountTxInternalByHashTests.java} (97%) rename src/test/java/io/goodforgod/api/etherscan/account/{AccountTxInternalTest.java => AccountTxInternalTests.java} (97%) rename src/test/java/io/goodforgod/api/etherscan/account/{AccountTxRc1155TokenTest.java => AccountTxRc1155TokenTests.java} (98%) rename src/test/java/io/goodforgod/api/etherscan/account/{AccountTxRc721TokenTest.java => AccountTxRc721TokenTests.java} (98%) rename src/test/java/io/goodforgod/api/etherscan/account/{AccountTxsTest.java => AccountTxsTests.java} (98%) rename src/test/java/io/goodforgod/api/etherscan/block/{BlockApiTest.java => BlockApiTests.java} (98%) rename src/test/java/io/goodforgod/api/etherscan/contract/{ContractApiTest.java => ContractApiTests.java} (96%) rename src/test/java/io/goodforgod/api/etherscan/gastracker/{GasTrackerApiTest.java => GasTrackerApiTests.java} (95%) rename src/test/java/io/goodforgod/api/etherscan/logs/{LogQueryBuilderTest.java => LogQueryBuilderTests.java} (99%) rename src/test/java/io/goodforgod/api/etherscan/logs/{LogsApiTest.java => LogsApiTests.java} (98%) rename src/test/java/io/goodforgod/api/etherscan/manager/{SemaphoreRequestQueueManagerTest.java => SemaphoreRequestQueueManagerTests.java} (70%) create mode 100644 src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java rename src/test/java/io/goodforgod/api/etherscan/proxy/{ProxyBlockApiTest.java => ProxyBlockApiTests.java} (97%) rename src/test/java/io/goodforgod/api/etherscan/proxy/{ProxyBlockLastNoApiTest.java => ProxyBlockLastNoApiTests.java} (85%) rename src/test/java/io/goodforgod/api/etherscan/proxy/{ProxyBlockUncleApiTest.java => ProxyBlockUncleApiTests.java} (94%) rename src/test/java/io/goodforgod/api/etherscan/proxy/{ProxyCallApiTest.java => ProxyCallApiTests.java} (97%) rename src/test/java/io/goodforgod/api/etherscan/proxy/{ProxyCodeApiTest.java => ProxyCodeApiTests.java} (95%) rename src/test/java/io/goodforgod/api/etherscan/proxy/{ProxyGasApiTest.java => ProxyGasApiTests.java} (96%) rename src/test/java/io/goodforgod/api/etherscan/proxy/{ProxyStorageApiTest.java => ProxyStorageApiTests.java} (95%) rename src/test/java/io/goodforgod/api/etherscan/proxy/{ProxyTxApiTest.java => ProxyTxApiTests.java} (98%) rename src/test/java/io/goodforgod/api/etherscan/proxy/{ProxyTxCountApiTest.java => ProxyTxCountApiTests.java} (96%) rename src/test/java/io/goodforgod/api/etherscan/proxy/{ProxyTxReceiptApiTest.java => ProxyTxReceiptApiTests.java} (97%) rename src/test/java/io/goodforgod/api/etherscan/proxy/{ProxyTxSendRawApiTest.java => ProxyTxSendRawApiTests.java} (95%) rename src/test/java/io/goodforgod/api/etherscan/statistic/{StatisticPriceApiTest.java => StatisticPriceApiTests.java} (93%) rename src/test/java/io/goodforgod/api/etherscan/statistic/{StatisticSupplyApiTest.java => StatisticSupplyApiTests.java} (93%) rename src/test/java/io/goodforgod/api/etherscan/statistic/{StatisticTokenSupplyApiTest.java => StatisticTokenSupplyApiTests.java} (94%) rename src/test/java/io/goodforgod/api/etherscan/transaction/{TransactionExecApiTest.java => TransactionExecApiTests.java} (96%) rename src/test/java/io/goodforgod/api/etherscan/transaction/{TransactionReceiptApiTest.java => TransactionReceiptApiTests.java} (95%) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index b4c0bb4..3eb55f0 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -32,9 +32,16 @@ jobs: run: ./gradlew spotlessCheck - name: Test + if: matrix.java == '11' run: ./gradlew test jacocoTestReport env: - API_KEY: ${{ secrets.API_KEY }} + API_KEY: ${{ secrets.ETHERSCAN_API_KEY_1 }} + + - name: Test + if: matrix.java == '17' + run: ./gradlew test jacocoTestReport + env: + API_KEY: ${{ secrets.ETHERSCAN_API_KEY_2 }} - name: SonarQube if: matrix.java == '17' diff --git a/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java b/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java index d568601..6b2740a 100644 --- a/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java +++ b/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java @@ -12,11 +12,8 @@ */ public interface RequestQueueManager extends AutoCloseable { - RequestQueueManager DEFAULT = new SemaphoreRequestQueueManager(1, Duration.ofMillis(5050L), - Duration.ofMillis(5050L), 0); - - RequestQueueManager PERSONAL = new SemaphoreRequestQueueManager(5, Duration.ofMillis(1050L), - Duration.ofMillis(1050L), 5); + RequestQueueManager DEFAULT = new SemaphoreRequestQueueManager(1, Duration.ofMillis(5005L)); + RequestQueueManager PERSONAL = new SemaphoreRequestQueueManager(5, Duration.ofMillis(1005L)); /** * Waits in queue for chance to take turn diff --git a/src/main/java/io/goodforgod/api/etherscan/manager/impl/SemaphoreRequestQueueManager.java b/src/main/java/io/goodforgod/api/etherscan/manager/impl/SemaphoreRequestQueueManager.java index a6e3037..2a3483c 100644 --- a/src/main/java/io/goodforgod/api/etherscan/manager/impl/SemaphoreRequestQueueManager.java +++ b/src/main/java/io/goodforgod/api/etherscan/manager/impl/SemaphoreRequestQueueManager.java @@ -21,18 +21,10 @@ public final class SemaphoreRequestQueueManager implements RequestQueueManager, private final long queueResetTimeInMillis; public SemaphoreRequestQueueManager(int size, Duration resetIn) { - this(size, resetIn, resetIn); - } - - public SemaphoreRequestQueueManager(int size, Duration resetIn, Duration delayIn) { - this(size, resetIn, delayIn, size); - } - - public SemaphoreRequestQueueManager(int size, Duration queueResetTimeIn, Duration delayIn, int initialSize) { - this.semaphore = new Semaphore(initialSize); - this.queueResetTimeInMillis = queueResetTimeIn.toMillis(); - this.executorService.scheduleAtFixedRate(releaseLocks(size + 1), - delayIn.toMillis(), queueResetTimeInMillis, TimeUnit.MILLISECONDS); + this.semaphore = new Semaphore(0); + this.queueResetTimeInMillis = resetIn.toMillis(); + this.executorService.scheduleAtFixedRate(releaseLocks(size), + resetIn.toMillis(), queueResetTimeInMillis, TimeUnit.MILLISECONDS); } @SuppressWarnings("java:S899") @@ -46,7 +38,13 @@ public void takeTurn() { } private Runnable releaseLocks(int toRelease) { - return () -> semaphore.release(toRelease); + return () -> { + int availablePermits = semaphore.availablePermits(); + int neededPermits = toRelease - availablePermits; + if (neededPermits > 0) { + semaphore.release(neededPermits); + } + }; } @Override diff --git a/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java b/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java index 7a5ef52..d63bc73 100644 --- a/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java +++ b/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java @@ -1,6 +1,7 @@ package io.goodforgod.api.etherscan; import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import java.util.Map; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; @@ -8,30 +9,37 @@ public class ApiRunner extends Assertions { private static final String DEFAULT_KEY = "YourApiKeyToken"; - private static final EtherScanAPI api; - private static final String apiKey; + private static final String API_KEY; + private static final EtherScanAPI API; static { - final String key = System.getenv("API_KEY"); - final RequestQueueManager queueManager = RequestQueueManager.DEFAULT; - - apiKey = (key == null || key.isEmpty()) - ? DEFAULT_KEY - : key; - api = EtherScanAPI.builder().withApiKey(ApiRunner.apiKey).withNetwork(EthNetworks.MAINNET).withQueue(queueManager) + API_KEY = System.getenv().entrySet().stream() + .filter(e -> e.getKey().startsWith("ETHERSCAN_API_KEY")) + .map(Map.Entry::getValue) + .findFirst() + .orElse(DEFAULT_KEY); + + final RequestQueueManager queueManager = (DEFAULT_KEY.equals(API_KEY)) + ? RequestQueueManager.DEFAULT + : RequestQueueManager.PERSONAL; + + API = EtherScanAPI.builder() + .withApiKey(ApiRunner.API_KEY) + .withNetwork(EthNetworks.MAINNET) + .withQueue(queueManager) .build(); } public static String getApiKey() { - return apiKey; + return API_KEY; } public static EtherScanAPI getApi() { - return api; + return API; } @AfterAll public static void cleanup() throws Exception { - api.close(); + API.close(); } } diff --git a/src/test/java/io/goodforgod/api/etherscan/EtherScanAPITests.java b/src/test/java/io/goodforgod/api/etherscan/EtherScanAPITests.java index 22c58d6..c50b03a 100644 --- a/src/test/java/io/goodforgod/api/etherscan/EtherScanAPITests.java +++ b/src/test/java/io/goodforgod/api/etherscan/EtherScanAPITests.java @@ -5,6 +5,7 @@ import io.goodforgod.api.etherscan.http.EthHttpClient; import io.goodforgod.api.etherscan.http.impl.UrlEthHttpClient; import io.goodforgod.api.etherscan.model.Balance; +import java.net.URI; import java.time.Duration; import java.util.concurrent.TimeUnit; import java.util.function.Supplier; @@ -16,7 +17,7 @@ */ class EtherScanAPITests extends ApiRunner { - private final EthNetworks network = EthNetworks.KOVAN; + private final EthNetworks network = EthNetworks.SEPOLIA; @Test void validKey() { @@ -46,14 +47,12 @@ void noTimeoutOnRead() { @Test void noTimeoutOnReadGroli() { - Supplier supplier = () -> new UrlEthHttpClient(Duration.ofMillis(300)); Balance balance = getApi().account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); assertNotNull(balance); } @Test void noTimeoutOnReadTobalala() { - Supplier supplier = () -> new UrlEthHttpClient(Duration.ofMillis(30000)); Balance balance = getApi().account().balance("0xF318ABc9A5a92357c4Fea8d082dade4D43e780B7"); assertNotNull(balance); } @@ -68,8 +67,12 @@ void noTimeoutUnlimitedAwait() { void timeout() throws InterruptedException { TimeUnit.SECONDS.sleep(5); Supplier supplier = () -> new UrlEthHttpClient(Duration.ofMillis(300), Duration.ofMillis(300)); - EtherScanAPI api = EtherScanAPI.builder().withApiKey(getApiKey()).withNetwork(EthNetworks.KOVAN).withHttpClient(supplier) + EtherScanAPI api = EtherScanAPI.builder() + .withApiKey(getApiKey()) + .withNetwork(() -> URI.create("https://api-unknown.etherscan.io/api")) + .withHttpClient(supplier) .build(); + assertThrows(EtherScanConnectionException.class, () -> api.account().blocksMined("0x0010f94b296A852aAac52EA6c5Ac72e03afD032D")); } diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceListTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceListTests.java similarity index 98% rename from src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceListTest.java rename to src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceListTests.java index cd3dac9..f611b62 100644 --- a/src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceListTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceListTests.java @@ -13,7 +13,7 @@ * @author GoodforGod * @since 03.11.2018 */ -class AccountBalanceListTest extends ApiRunner { +class AccountBalanceListTests extends ApiRunner { @Test void correct() { diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceTests.java similarity index 96% rename from src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceTest.java rename to src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceTests.java index 4c06c7c..f22a724 100644 --- a/src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceTests.java @@ -10,7 +10,7 @@ * @author GoodforGod * @since 03.11.2018 */ -class AccountBalanceTest extends ApiRunner { +class AccountBalanceTests extends ApiRunner { private final EtherScanAPI api = getApi(); diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountMinedBlocksTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountMinedBlocksTests.java similarity index 96% rename from src/test/java/io/goodforgod/api/etherscan/account/AccountMinedBlocksTest.java rename to src/test/java/io/goodforgod/api/etherscan/account/AccountMinedBlocksTests.java index 13d5075..3e19e96 100644 --- a/src/test/java/io/goodforgod/api/etherscan/account/AccountMinedBlocksTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountMinedBlocksTests.java @@ -11,7 +11,7 @@ * @author GoodforGod * @since 03.11.2018 */ -class AccountMinedBlocksTest extends ApiRunner { +class AccountMinedBlocksTests extends ApiRunner { private final EtherScanAPI api = getApi(); diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountTokenBalanceTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTokenBalanceTests.java similarity index 97% rename from src/test/java/io/goodforgod/api/etherscan/account/AccountTokenBalanceTest.java rename to src/test/java/io/goodforgod/api/etherscan/account/AccountTokenBalanceTests.java index 4df75f3..4a7d921 100644 --- a/src/test/java/io/goodforgod/api/etherscan/account/AccountTokenBalanceTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTokenBalanceTests.java @@ -10,7 +10,7 @@ * @author GoodforGod * @since 03.11.2018 */ -class AccountTokenBalanceTest extends ApiRunner { +class AccountTokenBalanceTests extends ApiRunner { private final EtherScanAPI api = getApi(); diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxErc20Test.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxErc20Tests.java similarity index 98% rename from src/test/java/io/goodforgod/api/etherscan/account/AccountTxErc20Test.java rename to src/test/java/io/goodforgod/api/etherscan/account/AccountTxErc20Tests.java index 0a94289..928b2e3 100644 --- a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxErc20Test.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxErc20Tests.java @@ -10,7 +10,7 @@ * @author GoodforGod * @since 03.11.2018 */ -class AccountTxErc20Test extends ApiRunner { +class AccountTxErc20Tests extends ApiRunner { @Test void correct() { diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalByHashTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalByHashTests.java similarity index 97% rename from src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalByHashTest.java rename to src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalByHashTests.java index 13036bc..eb06b60 100644 --- a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalByHashTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalByHashTests.java @@ -12,7 +12,7 @@ * @author GoodforGod * @since 03.11.2018 */ -class AccountTxInternalByHashTest extends ApiRunner { +class AccountTxInternalByHashTests extends ApiRunner { private final EtherScanAPI api = getApi(); diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalTests.java similarity index 97% rename from src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalTest.java rename to src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalTests.java index 6fb92b4..1d4220d 100644 --- a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxInternalTests.java @@ -10,7 +10,7 @@ * @author GoodforGod * @since 03.11.2018 */ -class AccountTxInternalTest extends ApiRunner { +class AccountTxInternalTests extends ApiRunner { @Test void correct() { diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc1155TokenTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc1155TokenTests.java similarity index 98% rename from src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc1155TokenTest.java rename to src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc1155TokenTests.java index ce3a680..0430dc8 100644 --- a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc1155TokenTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc1155TokenTests.java @@ -10,7 +10,7 @@ * @author GoodforGod * @since 14.05.2023 */ -class AccountTxRc1155TokenTest extends ApiRunner { +class AccountTxRc1155TokenTests extends ApiRunner { @Test void correct() { diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTests.java similarity index 98% rename from src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTest.java rename to src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTests.java index b7988db..9a5a322 100644 --- a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTests.java @@ -10,7 +10,7 @@ * @author NGuggs * @since 11.28.2021 */ -class AccountTxRc721TokenTest extends ApiRunner { +class AccountTxRc721TokenTests extends ApiRunner { @Test void correct() { diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxsTest.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxsTests.java similarity index 98% rename from src/test/java/io/goodforgod/api/etherscan/account/AccountTxsTest.java rename to src/test/java/io/goodforgod/api/etherscan/account/AccountTxsTests.java index a2cffd1..3ee8ad1 100644 --- a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxsTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxsTests.java @@ -10,7 +10,7 @@ * @author GoodforGod * @since 03.11.2018 */ -class AccountTxsTest extends ApiRunner { +class AccountTxsTests extends ApiRunner { @Test void correct() { diff --git a/src/test/java/io/goodforgod/api/etherscan/block/BlockApiTest.java b/src/test/java/io/goodforgod/api/etherscan/block/BlockApiTests.java similarity index 98% rename from src/test/java/io/goodforgod/api/etherscan/block/BlockApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/block/BlockApiTests.java index 8e3b529..3e84ab7 100644 --- a/src/test/java/io/goodforgod/api/etherscan/block/BlockApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/block/BlockApiTests.java @@ -9,7 +9,7 @@ * @author GoodforGod * @since 03.11.2018 */ -class BlockApiTest extends ApiRunner { +class BlockApiTests extends ApiRunner { @Test void correct() { diff --git a/src/test/java/io/goodforgod/api/etherscan/contract/ContractApiTest.java b/src/test/java/io/goodforgod/api/etherscan/contract/ContractApiTests.java similarity index 96% rename from src/test/java/io/goodforgod/api/etherscan/contract/ContractApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/contract/ContractApiTests.java index 62aa7da..4fd0fdb 100644 --- a/src/test/java/io/goodforgod/api/etherscan/contract/ContractApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/contract/ContractApiTests.java @@ -9,7 +9,7 @@ * @author GoodforGod * @since 03.11.2018 */ -class ContractApiTest extends ApiRunner { +class ContractApiTests extends ApiRunner { @Test void correct() { diff --git a/src/test/java/io/goodforgod/api/etherscan/gastracker/GasTrackerApiTest.java b/src/test/java/io/goodforgod/api/etherscan/gastracker/GasTrackerApiTests.java similarity index 95% rename from src/test/java/io/goodforgod/api/etherscan/gastracker/GasTrackerApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/gastracker/GasTrackerApiTests.java index ec904a6..1d92eb4 100644 --- a/src/test/java/io/goodforgod/api/etherscan/gastracker/GasTrackerApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/gastracker/GasTrackerApiTests.java @@ -10,7 +10,7 @@ * @author GoodforGod * @since 14.05.2023 */ -class GasTrackerApiTest extends ApiRunner { +class GasTrackerApiTests extends ApiRunner { @Test void estimate() { diff --git a/src/test/java/io/goodforgod/api/etherscan/logs/LogQueryBuilderTest.java b/src/test/java/io/goodforgod/api/etherscan/logs/LogQueryBuilderTests.java similarity index 99% rename from src/test/java/io/goodforgod/api/etherscan/logs/LogQueryBuilderTest.java rename to src/test/java/io/goodforgod/api/etherscan/logs/LogQueryBuilderTests.java index 752c34c..339f07e 100644 --- a/src/test/java/io/goodforgod/api/etherscan/logs/LogQueryBuilderTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/logs/LogQueryBuilderTests.java @@ -10,7 +10,7 @@ * @author GoodforGod * @since 03.11.2018 */ -class LogQueryBuilderTest extends ApiRunner { +class LogQueryBuilderTests extends ApiRunner { @Test void singleCorrect() { diff --git a/src/test/java/io/goodforgod/api/etherscan/logs/LogsApiTest.java b/src/test/java/io/goodforgod/api/etherscan/logs/LogsApiTests.java similarity index 98% rename from src/test/java/io/goodforgod/api/etherscan/logs/LogsApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/logs/LogsApiTests.java index 7d9fe64..0197c5f 100644 --- a/src/test/java/io/goodforgod/api/etherscan/logs/LogsApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/logs/LogsApiTests.java @@ -14,7 +14,7 @@ * @author GoodforGod * @since 03.11.2018 */ -class LogsApiTest extends ApiRunner { +class LogsApiTests extends ApiRunner { static Stream source() { LogQuery single = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") diff --git a/src/test/java/io/goodforgod/api/etherscan/manager/SemaphoreRequestQueueManagerTest.java b/src/test/java/io/goodforgod/api/etherscan/manager/SemaphoreRequestQueueManagerTests.java similarity index 70% rename from src/test/java/io/goodforgod/api/etherscan/manager/SemaphoreRequestQueueManagerTest.java rename to src/test/java/io/goodforgod/api/etherscan/manager/SemaphoreRequestQueueManagerTests.java index 0d6daf6..183c442 100644 --- a/src/test/java/io/goodforgod/api/etherscan/manager/SemaphoreRequestQueueManagerTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/manager/SemaphoreRequestQueueManagerTests.java @@ -11,7 +11,7 @@ * @author GoodforGod * @since 03.11.2018 */ -class SemaphoreRequestQueueManagerTest extends ApiRunner { +class SemaphoreRequestQueueManagerTests extends ApiRunner { @Test void fakeManager() { @@ -37,20 +37,9 @@ void queueManager() { @Test @Timeout(4500) void queueManagerWithDelay() { - RequestQueueManager requestQueueManager = new SemaphoreRequestQueueManager(1, Duration.ofSeconds(2), - Duration.ofSeconds(2)); + RequestQueueManager requestQueueManager = new SemaphoreRequestQueueManager(1, Duration.ofSeconds(2)); requestQueueManager.takeTurn(); requestQueueManager.takeTurn(); assertNotNull(requestQueueManager); } - - @Test - void queueManagerTimeout() { - RequestQueueManager requestQueueManager = new SemaphoreRequestQueueManager(1, Duration.ofSeconds(3)); - requestQueueManager.takeTurn(); - long start = System.currentTimeMillis(); - requestQueueManager.takeTurn(); - long end = System.currentTimeMillis(); - assertEquals(3, Math.round((double) (end - start) / 1000)); - } } diff --git a/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java b/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java new file mode 100644 index 0000000..6f5fc22 --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java @@ -0,0 +1,7 @@ +package io.goodforgod.api.etherscan.model; + +/** + * @author Anton Kurako (GoodforGod) + * @since 14.05.2023 + */ +class ModelBuilderTests {} diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTests.java similarity index 97% rename from src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTests.java index e31aab8..44d786d 100644 --- a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTests.java @@ -12,11 +12,11 @@ * @author GoodforGod * @since 03.11.2018 */ -class ProxyBlockApiTest extends ApiRunner { +class ProxyBlockApiTests extends ApiRunner { private final EtherScanAPI api; - ProxyBlockApiTest() { + ProxyBlockApiTests() { final RequestQueueManager queueManager = RequestQueueManager.DEFAULT; this.api = EtherScanAPI.builder().withApiKey(getApiKey()).withNetwork(EthNetworks.MAINNET).withQueue(queueManager) .build(); diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockLastNoApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockLastNoApiTests.java similarity index 85% rename from src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockLastNoApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockLastNoApiTests.java index c4f5e31..568d9ae 100644 --- a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockLastNoApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockLastNoApiTests.java @@ -7,7 +7,7 @@ * @author GoodforGod * @since 13.11.2018 */ -class ProxyBlockLastNoApiTest extends ApiRunner { +class ProxyBlockLastNoApiTests extends ApiRunner { @Test void correct() { diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockUncleApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockUncleApiTests.java similarity index 94% rename from src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockUncleApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockUncleApiTests.java index c575072..01725c5 100644 --- a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockUncleApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockUncleApiTests.java @@ -9,7 +9,7 @@ * @author GoodforGod * @since 13.11.2018 */ -class ProxyBlockUncleApiTest extends ApiRunner { +class ProxyBlockUncleApiTests extends ApiRunner { @Test void correct() { diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCallApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCallApiTests.java similarity index 97% rename from src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCallApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCallApiTests.java index 67e7682..d5168c6 100644 --- a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCallApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCallApiTests.java @@ -11,7 +11,7 @@ * @author GoodforGod * @since 03.11.2018 */ -class ProxyCallApiTest extends ApiRunner { +class ProxyCallApiTests extends ApiRunner { @Test void correct() { diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCodeApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCodeApiTests.java similarity index 95% rename from src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCodeApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCodeApiTests.java index c9dab25..1e3c696 100644 --- a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCodeApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyCodeApiTests.java @@ -10,7 +10,7 @@ * @author GoodforGod * @since 03.11.2018 */ -class ProxyCodeApiTest extends ApiRunner { +class ProxyCodeApiTests extends ApiRunner { @Test void correct() { diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyGasApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyGasApiTests.java similarity index 96% rename from src/test/java/io/goodforgod/api/etherscan/proxy/ProxyGasApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/proxy/ProxyGasApiTests.java index 1b40705..0ab2a77 100644 --- a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyGasApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyGasApiTests.java @@ -9,7 +9,7 @@ * @author GoodforGod * @since 03.11.2018 */ -class ProxyGasApiTest extends ApiRunner { +class ProxyGasApiTests extends ApiRunner { @Test void correctPrice() { diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyStorageApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyStorageApiTests.java similarity index 95% rename from src/test/java/io/goodforgod/api/etherscan/proxy/ProxyStorageApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/proxy/ProxyStorageApiTests.java index 2580e22..3c6d221 100644 --- a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyStorageApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyStorageApiTests.java @@ -9,7 +9,7 @@ * @author GoodforGod * @since 03.11.2018 */ -class ProxyStorageApiTest extends ApiRunner { +class ProxyStorageApiTests extends ApiRunner { @Test void correct() { diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxApiTests.java similarity index 98% rename from src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxApiTests.java index d6790bd..6c7dbb7 100644 --- a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxApiTests.java @@ -10,7 +10,7 @@ * @author GoodforGod * @since 03.11.2018 */ -class ProxyTxApiTest extends ApiRunner { +class ProxyTxApiTests extends ApiRunner { @Test void correctByHash() { diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxCountApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxCountApiTests.java similarity index 96% rename from src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxCountApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxCountApiTests.java index a2327da..95ed859 100644 --- a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxCountApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxCountApiTests.java @@ -8,7 +8,7 @@ * @author GoodforGod * @since 03.11.2018 */ -class ProxyTxCountApiTest extends ApiRunner { +class ProxyTxCountApiTests extends ApiRunner { @Test void correctSended() { diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxReceiptApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxReceiptApiTests.java similarity index 97% rename from src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxReceiptApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxReceiptApiTests.java index ba6370c..7a6624c 100644 --- a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxReceiptApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxReceiptApiTests.java @@ -10,7 +10,7 @@ * @author GoodforGod * @since 03.11.2018 */ -class ProxyTxReceiptApiTest extends ApiRunner { +class ProxyTxReceiptApiTests extends ApiRunner { @Test void correct() { diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxSendRawApiTest.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxSendRawApiTests.java similarity index 95% rename from src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxSendRawApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxSendRawApiTests.java index 9f69060..3910bf8 100644 --- a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxSendRawApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxSendRawApiTests.java @@ -11,7 +11,7 @@ * @since 03.11.2018 */ // TODO contact etherscan and ask about method behavior -class ProxyTxSendRawApiTest extends ApiRunner { +class ProxyTxSendRawApiTests extends ApiRunner { void correct() { Optional sendRaw = getApi().proxy() diff --git a/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTest.java b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTests.java similarity index 93% rename from src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTests.java index eb43b6e..37e0ec0 100644 --- a/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTests.java @@ -8,7 +8,7 @@ * @author GoodforGod * @since 03.11.2018 */ -class StatisticPriceApiTest extends ApiRunner { +class StatisticPriceApiTests extends ApiRunner { @Test void correct() { diff --git a/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyApiTest.java b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyApiTests.java similarity index 93% rename from src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyApiTests.java index c1e8e58..fa79028 100644 --- a/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyApiTests.java @@ -9,7 +9,7 @@ * @author GoodforGod * @since 03.11.2018 */ -class StatisticSupplyApiTest extends ApiRunner { +class StatisticSupplyApiTests extends ApiRunner { @Test void correct() { diff --git a/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticTokenSupplyApiTest.java b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticTokenSupplyApiTests.java similarity index 94% rename from src/test/java/io/goodforgod/api/etherscan/statistic/StatisticTokenSupplyApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/statistic/StatisticTokenSupplyApiTests.java index 84c086a..07f8eca 100644 --- a/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticTokenSupplyApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticTokenSupplyApiTests.java @@ -9,7 +9,7 @@ * @author GoodforGod * @since 03.11.2018 */ -class StatisticTokenSupplyApiTest extends ApiRunner { +class StatisticTokenSupplyApiTests extends ApiRunner { @Test void correct() { diff --git a/src/test/java/io/goodforgod/api/etherscan/transaction/TransactionExecApiTest.java b/src/test/java/io/goodforgod/api/etherscan/transaction/TransactionExecApiTests.java similarity index 96% rename from src/test/java/io/goodforgod/api/etherscan/transaction/TransactionExecApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/transaction/TransactionExecApiTests.java index a2a5860..eb595c3 100644 --- a/src/test/java/io/goodforgod/api/etherscan/transaction/TransactionExecApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/transaction/TransactionExecApiTests.java @@ -10,7 +10,7 @@ * @author GoodforGod * @since 03.11.2018 */ -class TransactionExecApiTest extends ApiRunner { +class TransactionExecApiTests extends ApiRunner { @Test void correct() { diff --git a/src/test/java/io/goodforgod/api/etherscan/transaction/TransactionReceiptApiTest.java b/src/test/java/io/goodforgod/api/etherscan/transaction/TransactionReceiptApiTests.java similarity index 95% rename from src/test/java/io/goodforgod/api/etherscan/transaction/TransactionReceiptApiTest.java rename to src/test/java/io/goodforgod/api/etherscan/transaction/TransactionReceiptApiTests.java index 83ca5af..8ff0817 100644 --- a/src/test/java/io/goodforgod/api/etherscan/transaction/TransactionReceiptApiTest.java +++ b/src/test/java/io/goodforgod/api/etherscan/transaction/TransactionReceiptApiTests.java @@ -9,7 +9,7 @@ * @author GoodforGod * @since 03.11.2018 */ -class TransactionReceiptApiTest extends ApiRunner { +class TransactionReceiptApiTests extends ApiRunner { @Test void correct() { From 55788d505b74d71978001d9fae8f929b0a7ec8ab Mon Sep 17 00:00:00 2001 From: guggio Date: Sun, 17 Jul 2022 12:10:23 +0300 Subject: [PATCH 25/61] [2.0.0-SNAPSHOT] Refactoring of token transfers - Inclusion of tokenID in Erc721 transfers - Support for Erc1155 transfers (cherry picked from commit ca4b7d5eca7d9d08688af3e38c6702f77c149d2d) --- .../io/api/etherscan/model/BaseTxToken.java | 59 +++++++++++++++++++ .../io/api/etherscan/model/TxErc1155.java | 25 ++++++++ .../java/io/api/etherscan/model/TxErc20.java | 19 ++++++ .../java/io/api/etherscan/model/TxErc721.java | 25 ++++++++ .../model/response/TxErc1155ResponseTO.java | 11 ---- .../model/response/TxErc20ResponseTO.java | 11 ---- .../model/response/TxErc721ResponseTO.java | 11 ---- 7 files changed, 128 insertions(+), 33 deletions(-) create mode 100644 src/main/java/io/api/etherscan/model/BaseTxToken.java create mode 100644 src/main/java/io/api/etherscan/model/TxErc1155.java create mode 100644 src/main/java/io/api/etherscan/model/TxErc20.java create mode 100644 src/main/java/io/api/etherscan/model/TxErc721.java delete mode 100644 src/main/java/io/goodforgod/api/etherscan/model/response/TxErc1155ResponseTO.java delete mode 100644 src/main/java/io/goodforgod/api/etherscan/model/response/TxErc20ResponseTO.java delete mode 100644 src/main/java/io/goodforgod/api/etherscan/model/response/TxErc721ResponseTO.java diff --git a/src/main/java/io/api/etherscan/model/BaseTxToken.java b/src/main/java/io/api/etherscan/model/BaseTxToken.java new file mode 100644 index 0000000..e1965ce --- /dev/null +++ b/src/main/java/io/api/etherscan/model/BaseTxToken.java @@ -0,0 +1,59 @@ +package io.api.etherscan.model; + +public abstract class BaseTxToken extends BaseTx { + + private long nonce; + private String blockHash; + private String tokenName; + private String tokenSymbol; + private int transactionIndex; + private long gasPrice; + private long cumulativeGasUsed; + private long confirmations; + + public long getNonce() { + return nonce; + } + + public String getBlockHash() { + return blockHash; + } + + public String getTokenName() { + return tokenName; + } + + public String getTokenSymbol() { + return tokenSymbol; + } + + public int getTransactionIndex() { + return transactionIndex; + } + + public long getGasPrice() { + return gasPrice; + } + + public long getCumulativeGasUsed() { + return cumulativeGasUsed; + } + + public long getConfirmations() { + return confirmations; + } + + @Override + public String toString() { + return "BaseTxToken{" + + "nonce=" + nonce + + ", blockHash='" + blockHash + '\'' + + ", tokenName='" + tokenName + '\'' + + ", tokenSymbol='" + tokenSymbol + '\'' + + ", transactionIndex=" + transactionIndex + + ", gasPrice=" + gasPrice + + ", cumulativeGasUsed=" + cumulativeGasUsed + + ", confirmations=" + confirmations + + '}' + super.toString(); + } +} diff --git a/src/main/java/io/api/etherscan/model/TxErc1155.java b/src/main/java/io/api/etherscan/model/TxErc1155.java new file mode 100644 index 0000000..b926b1b --- /dev/null +++ b/src/main/java/io/api/etherscan/model/TxErc1155.java @@ -0,0 +1,25 @@ +package io.api.etherscan.model; + +import java.math.BigInteger; + +public class TxErc1155 extends BaseTxToken { + + private BigInteger tokenID; + private BigInteger tokenValue; + + public BigInteger getTokenID() { + return tokenID; + } + + public BigInteger getTokenValue() { + return tokenValue; + } + + @Override + public String toString() { + return "TxErc1155{" + + "tokenID=" + tokenID + + ", tokenValue=" + tokenValue + + '}' + super.toString(); + } +} diff --git a/src/main/java/io/api/etherscan/model/TxErc20.java b/src/main/java/io/api/etherscan/model/TxErc20.java new file mode 100644 index 0000000..9c4e65e --- /dev/null +++ b/src/main/java/io/api/etherscan/model/TxErc20.java @@ -0,0 +1,19 @@ +package io.api.etherscan.model; + +import java.math.BigInteger; + +public class TxErc20 extends BaseTxToken { + + private BigInteger tokenDecimal; + + public BigInteger getTokenDecimal() { + return tokenDecimal; + } + + @Override + public String toString() { + return "TxErc20{" + + "tokenDecimal=" + tokenDecimal + + '}' + super.toString(); + } +} diff --git a/src/main/java/io/api/etherscan/model/TxErc721.java b/src/main/java/io/api/etherscan/model/TxErc721.java new file mode 100644 index 0000000..9f70c3d --- /dev/null +++ b/src/main/java/io/api/etherscan/model/TxErc721.java @@ -0,0 +1,25 @@ +package io.api.etherscan.model; + +import java.math.BigInteger; + +public class TxErc721 extends BaseTxToken { + + private BigInteger tokenID; + private BigInteger tokenDecimal; + + public BigInteger getTokenID() { + return tokenID; + } + + public BigInteger getTokenDecimal() { + return tokenDecimal; + } + + @Override + public String toString() { + return "TxErc721{" + + "tokenID=" + tokenID + + ", tokenDecimal=" + tokenDecimal + + '}' + super.toString(); + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc1155ResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc1155ResponseTO.java deleted file mode 100644 index 994d2cd..0000000 --- a/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc1155ResponseTO.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.goodforgod.api.etherscan.model.response; - -import io.goodforgod.api.etherscan.model.TxErc1155; - -/** - * @author GoodforGod - * @since 29.10.2018 - */ -public class TxErc1155ResponseTO extends BaseListResponseTO { - -} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc20ResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc20ResponseTO.java deleted file mode 100644 index d5d3f6e..0000000 --- a/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc20ResponseTO.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.goodforgod.api.etherscan.model.response; - -import io.goodforgod.api.etherscan.model.TxErc20; - -/** - * @author GoodforGod - * @since 29.10.2018 - */ -public class TxErc20ResponseTO extends BaseListResponseTO { - -} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc721ResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc721ResponseTO.java deleted file mode 100644 index 2a9403f..0000000 --- a/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc721ResponseTO.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.goodforgod.api.etherscan.model.response; - -import io.goodforgod.api.etherscan.model.TxErc721; - -/** - * @author GoodforGod - * @since 29.10.2018 - */ -public class TxErc721ResponseTO extends BaseListResponseTO { - -} From 8ec7d9349b6cbb07b97d21174216dea87a2111f4 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 14 May 2023 03:33:45 +0300 Subject: [PATCH 26/61] [2.0.0-SNAPSHOT] Refactoring of token transfers - Inclusion of tokenID in Erc721 transfers - Support for Erc1155 transfers (cherry picked from commit ca4b7d5eca7d9d08688af3e38c6702f77c149d2d) --- .../io/api/etherscan/model/BaseTxToken.java | 59 ------------------- .../io/api/etherscan/model/TxErc1155.java | 25 -------- .../java/io/api/etherscan/model/TxErc20.java | 19 ------ .../java/io/api/etherscan/model/TxErc721.java | 25 -------- 4 files changed, 128 deletions(-) delete mode 100644 src/main/java/io/api/etherscan/model/BaseTxToken.java delete mode 100644 src/main/java/io/api/etherscan/model/TxErc1155.java delete mode 100644 src/main/java/io/api/etherscan/model/TxErc20.java delete mode 100644 src/main/java/io/api/etherscan/model/TxErc721.java diff --git a/src/main/java/io/api/etherscan/model/BaseTxToken.java b/src/main/java/io/api/etherscan/model/BaseTxToken.java deleted file mode 100644 index e1965ce..0000000 --- a/src/main/java/io/api/etherscan/model/BaseTxToken.java +++ /dev/null @@ -1,59 +0,0 @@ -package io.api.etherscan.model; - -public abstract class BaseTxToken extends BaseTx { - - private long nonce; - private String blockHash; - private String tokenName; - private String tokenSymbol; - private int transactionIndex; - private long gasPrice; - private long cumulativeGasUsed; - private long confirmations; - - public long getNonce() { - return nonce; - } - - public String getBlockHash() { - return blockHash; - } - - public String getTokenName() { - return tokenName; - } - - public String getTokenSymbol() { - return tokenSymbol; - } - - public int getTransactionIndex() { - return transactionIndex; - } - - public long getGasPrice() { - return gasPrice; - } - - public long getCumulativeGasUsed() { - return cumulativeGasUsed; - } - - public long getConfirmations() { - return confirmations; - } - - @Override - public String toString() { - return "BaseTxToken{" + - "nonce=" + nonce + - ", blockHash='" + blockHash + '\'' + - ", tokenName='" + tokenName + '\'' + - ", tokenSymbol='" + tokenSymbol + '\'' + - ", transactionIndex=" + transactionIndex + - ", gasPrice=" + gasPrice + - ", cumulativeGasUsed=" + cumulativeGasUsed + - ", confirmations=" + confirmations + - '}' + super.toString(); - } -} diff --git a/src/main/java/io/api/etherscan/model/TxErc1155.java b/src/main/java/io/api/etherscan/model/TxErc1155.java deleted file mode 100644 index b926b1b..0000000 --- a/src/main/java/io/api/etherscan/model/TxErc1155.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.api.etherscan.model; - -import java.math.BigInteger; - -public class TxErc1155 extends BaseTxToken { - - private BigInteger tokenID; - private BigInteger tokenValue; - - public BigInteger getTokenID() { - return tokenID; - } - - public BigInteger getTokenValue() { - return tokenValue; - } - - @Override - public String toString() { - return "TxErc1155{" + - "tokenID=" + tokenID + - ", tokenValue=" + tokenValue + - '}' + super.toString(); - } -} diff --git a/src/main/java/io/api/etherscan/model/TxErc20.java b/src/main/java/io/api/etherscan/model/TxErc20.java deleted file mode 100644 index 9c4e65e..0000000 --- a/src/main/java/io/api/etherscan/model/TxErc20.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.api.etherscan.model; - -import java.math.BigInteger; - -public class TxErc20 extends BaseTxToken { - - private BigInteger tokenDecimal; - - public BigInteger getTokenDecimal() { - return tokenDecimal; - } - - @Override - public String toString() { - return "TxErc20{" + - "tokenDecimal=" + tokenDecimal + - '}' + super.toString(); - } -} diff --git a/src/main/java/io/api/etherscan/model/TxErc721.java b/src/main/java/io/api/etherscan/model/TxErc721.java deleted file mode 100644 index 9f70c3d..0000000 --- a/src/main/java/io/api/etherscan/model/TxErc721.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.api.etherscan.model; - -import java.math.BigInteger; - -public class TxErc721 extends BaseTxToken { - - private BigInteger tokenID; - private BigInteger tokenDecimal; - - public BigInteger getTokenID() { - return tokenID; - } - - public BigInteger getTokenDecimal() { - return tokenDecimal; - } - - @Override - public String toString() { - return "TxErc721{" + - "tokenID=" + tokenID + - ", tokenDecimal=" + tokenDecimal + - '}' + super.toString(); - } -} From a9dd8e0d2d84b64fef15980715c15220f3cd4f9b Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 14 May 2023 03:45:36 +0300 Subject: [PATCH 27/61] [2.0.0-SNAPSHOT] Javadoc improvements --- .../api/etherscan/EthScanAPIBuilder.java | 5 ++--- .../api/etherscan/http/impl/UrlEthHttpClient.java | 5 +++-- .../etherscan/manager/RequestQueueManager.java | 15 +++++++++++++-- .../io/goodforgod/api/etherscan/ApiRunner.java | 4 ++-- .../api/etherscan/proxy/ProxyBlockApiTests.java | 2 +- 5 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java b/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java index 501bdb1..c9c1102 100644 --- a/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java +++ b/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java @@ -5,7 +5,6 @@ import io.goodforgod.api.etherscan.http.EthHttpClient; import io.goodforgod.api.etherscan.http.impl.UrlEthHttpClient; import io.goodforgod.api.etherscan.manager.RequestQueueManager; -import io.goodforgod.api.etherscan.manager.impl.FakeRequestQueueManager; import io.goodforgod.api.etherscan.util.BasicUtils; import io.goodforgod.gson.configuration.GsonConfiguration; import java.util.function.Supplier; @@ -24,7 +23,7 @@ final class EthScanAPIBuilder implements EtherScanAPI.Builder { private String apiKey = DEFAULT_KEY; private EthNetwork ethNetwork = EthNetworks.MAINNET; - private RequestQueueManager queueManager = RequestQueueManager.DEFAULT; + private RequestQueueManager queueManager = RequestQueueManager.ANONYMOUS; private Supplier ethHttpClientSupplier = DEFAULT_SUPPLIER; private Supplier converterSupplier = () -> new Converter() { @@ -42,7 +41,7 @@ public EtherScanAPI.Builder withApiKey(@NotNull String apiKey) { this.apiKey = apiKey; if (!DEFAULT_KEY.equals(apiKey)) { - queueManager = new FakeRequestQueueManager(); + queueManager = RequestQueueManager.UNLIMITED; } return this; } diff --git a/src/main/java/io/goodforgod/api/etherscan/http/impl/UrlEthHttpClient.java b/src/main/java/io/goodforgod/api/etherscan/http/impl/UrlEthHttpClient.java index 4178be7..57c970b 100644 --- a/src/main/java/io/goodforgod/api/etherscan/http/impl/UrlEthHttpClient.java +++ b/src/main/java/io/goodforgod/api/etherscan/http/impl/UrlEthHttpClient.java @@ -20,6 +20,7 @@ import java.util.Map; import java.util.zip.GZIPInputStream; import java.util.zip.InflaterInputStream; +import org.jetbrains.annotations.NotNull; /** * Http client implementation @@ -82,7 +83,7 @@ private HttpURLConnection buildConnection(URI uri, String method) throws IOExcep } @Override - public String get(URI uri) { + public @NotNull String get(@NotNull URI uri) { try { final HttpURLConnection connection = buildConnection(uri, "GET"); final int status = connection.getResponseCode(); @@ -105,7 +106,7 @@ public String get(URI uri) { } @Override - public String post(URI uri, byte[] body) { + public @NotNull String post(@NotNull URI uri, byte[] body) { try { final HttpURLConnection connection = buildConnection(uri, "POST"); final int contentLength = body.length; diff --git a/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java b/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java index 6b2740a..f19603f 100644 --- a/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java +++ b/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java @@ -1,5 +1,6 @@ package io.goodforgod.api.etherscan.manager; +import io.goodforgod.api.etherscan.manager.impl.FakeRequestQueueManager; import io.goodforgod.api.etherscan.manager.impl.SemaphoreRequestQueueManager; import java.time.Duration; @@ -12,8 +13,18 @@ */ public interface RequestQueueManager extends AutoCloseable { - RequestQueueManager DEFAULT = new SemaphoreRequestQueueManager(1, Duration.ofMillis(5005L)); - RequestQueueManager PERSONAL = new SemaphoreRequestQueueManager(5, Duration.ofMillis(1005L)); + /** + * Is used by default when no API KEY is provided + */ + RequestQueueManager ANONYMOUS = new SemaphoreRequestQueueManager(1, Duration.ofMillis(5005L)); + + /** + * Is available for all registered free API KEYs + * Free API KEY + */ + RequestQueueManager FREE_PLAN = new SemaphoreRequestQueueManager(5, Duration.ofMillis(1005L)); + + RequestQueueManager UNLIMITED = new FakeRequestQueueManager(); /** * Waits in queue for chance to take turn diff --git a/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java b/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java index d63bc73..d28a8e0 100644 --- a/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java +++ b/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java @@ -20,8 +20,8 @@ public class ApiRunner extends Assertions { .orElse(DEFAULT_KEY); final RequestQueueManager queueManager = (DEFAULT_KEY.equals(API_KEY)) - ? RequestQueueManager.DEFAULT - : RequestQueueManager.PERSONAL; + ? RequestQueueManager.ANONYMOUS + : RequestQueueManager.FREE_PLAN; API = EtherScanAPI.builder() .withApiKey(ApiRunner.API_KEY) diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTests.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTests.java index 44d786d..10dc6fd 100644 --- a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTests.java @@ -17,7 +17,7 @@ class ProxyBlockApiTests extends ApiRunner { private final EtherScanAPI api; ProxyBlockApiTests() { - final RequestQueueManager queueManager = RequestQueueManager.DEFAULT; + final RequestQueueManager queueManager = RequestQueueManager.ANONYMOUS; this.api = EtherScanAPI.builder().withApiKey(getApiKey()).withNetwork(EthNetworks.MAINNET).withQueue(queueManager) .build(); } From 225b21165f9bfa6defc4566a2f858f6f010498a0 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 14 May 2023 12:47:36 +0300 Subject: [PATCH 28/61] [2.0.0-SNAPSHOT] Tx Responses restored GasOracle refactored and improved ModelBuilderTests added --- .../goodforgod/api/etherscan/model/Block.java | 2 + .../api/etherscan/model/BlockUncle.java | 6 + .../api/etherscan/model/GasOracle.java | 72 +++-- .../goodforgod/api/etherscan/model/Log.java | 2 + .../goodforgod/api/etherscan/model/Price.java | 10 +- .../api/etherscan/model/Status.java | 2 + .../io/goodforgod/api/etherscan/model/Tx.java | 2 + .../api/etherscan/model/TxErc1155.java | 18 +- .../api/etherscan/model/TxErc20.java | 18 +- .../api/etherscan/model/TxErc721.java | 18 +- .../api/etherscan/model/TxInternal.java | 4 +- .../api/etherscan/model/proxy/BlockProxy.java | 2 + .../etherscan/model/proxy/ReceiptProxy.java | 2 + .../api/etherscan/model/proxy/TxProxy.java | 2 + .../model/response/TxErc1155ResponseTO.java | 11 + .../model/response/TxErc20ResponseTO.java | 11 + .../model/response/TxErc721ResponseTO.java | 11 + .../api/etherscan/block/BlockApiTests.java | 2 +- .../etherscan/model/ModelBuilderTests.java | 265 +++++++++++++++++- .../etherscan/proxy/ProxyBlockApiTests.java | 2 +- .../api/etherscan/proxy/ProxyTxApiTests.java | 2 +- .../proxy/ProxyTxReceiptApiTests.java | 2 +- .../statistic/StatisticPriceApiTests.java | 2 +- .../transaction/TransactionExecApiTests.java | 2 +- 24 files changed, 407 insertions(+), 63 deletions(-) create mode 100644 src/main/java/io/goodforgod/api/etherscan/model/response/TxErc1155ResponseTO.java create mode 100644 src/main/java/io/goodforgod/api/etherscan/model/response/TxErc20ResponseTO.java create mode 100644 src/main/java/io/goodforgod/api/etherscan/model/response/TxErc721ResponseTO.java diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Block.java b/src/main/java/io/goodforgod/api/etherscan/model/Block.java index 129ca39..d46fb44 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Block.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Block.java @@ -18,6 +18,8 @@ public class Block { @Expose(deserialize = false, serialize = false) LocalDateTime _timeStamp; + protected Block() {} + // public long getBlockNumber() { return blockNumber; diff --git a/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java b/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java index 5cf1a3e..02ddc28 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java @@ -18,6 +18,8 @@ public static class Uncle { private BigInteger blockreward; private int unclePosition; + private Uncle() {} + // public String getMiner() { return miner; @@ -113,6 +115,10 @@ public Uncle build() { private List uncles; private String uncleInclusionReward; + protected BlockUncle() { + super(); + } + // public boolean isEmpty() { return getBlockNumber() == 0 && getBlockReward() == null diff --git a/src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java b/src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java index 69fa6b5..67dd82a 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java @@ -1,7 +1,11 @@ package io.goodforgod.api.etherscan.model; +import java.math.BigDecimal; import java.math.BigInteger; +import java.util.Arrays; +import java.util.List; import java.util.Objects; +import java.util.stream.Collectors; /** * @author Abhay Gupta @@ -16,28 +20,32 @@ public class GasOracle { private Double suggestBaseFee; private String gasUsedRatio; + protected GasOracle() {} + public Long getLastBlock() { return LastBlock; } - public BigInteger getSafeGasPriceInWei() { - return BigInteger.valueOf(SafeGasPrice).multiply(BigInteger.TEN.pow(9)); + public Wei getSafeGasPriceInWei() { + return new Wei(BigInteger.valueOf(SafeGasPrice).multiply(BigInteger.TEN.pow(9))); } - public BigInteger getProposeGasPriceInWei() { - return BigInteger.valueOf(ProposeGasPrice).multiply(BigInteger.TEN.pow(9)); + public Wei getProposeGasPriceInWei() { + return new Wei(BigInteger.valueOf(ProposeGasPrice).multiply(BigInteger.TEN.pow(9))); } - public BigInteger getFastGasPriceInWei() { - return BigInteger.valueOf(FastGasPrice).multiply(BigInteger.TEN.pow(9)); + public Wei getFastGasPriceInWei() { + return new Wei(BigInteger.valueOf(FastGasPrice).multiply(BigInteger.TEN.pow(9))); } public Double getSuggestBaseFee() { return suggestBaseFee; } - public String getGasUsedRatio() { - return gasUsedRatio; + public List getGasUsedRatio() { + return Arrays.stream(gasUsedRatio.split(",")) + .map(BigDecimal::new) + .collect(Collectors.toList()); } @Override @@ -75,32 +83,32 @@ public static GasOracleBuilder builder() { public static final class GasOracleBuilder { - private Long LastBlock; - private Integer SafeGasPrice; - private Integer ProposeGasPrice; - private Integer FastGasPrice; + private Long lastBlock; + private Wei safeGasPrice; + private Wei proposeGasPrice; + private Wei fastGasPrice; private Double suggestBaseFee; - private String gasUsedRatio; + private List gasUsedRatio; private GasOracleBuilder() {} - public GasOracleBuilder withLastBlock(Long LastBlock) { - this.LastBlock = LastBlock; + public GasOracleBuilder withLastBlock(Long lastBlock) { + this.lastBlock = lastBlock; return this; } - public GasOracleBuilder withSafeGasPrice(Integer SafeGasPrice) { - this.SafeGasPrice = SafeGasPrice; + public GasOracleBuilder withSafeGasPrice(Wei safeGasPrice) { + this.safeGasPrice = safeGasPrice; return this; } - public GasOracleBuilder withProposeGasPrice(Integer ProposeGasPrice) { - this.ProposeGasPrice = ProposeGasPrice; + public GasOracleBuilder withProposeGasPrice(Wei proposeGasPrice) { + this.proposeGasPrice = proposeGasPrice; return this; } - public GasOracleBuilder withFastGasPrice(Integer FastGasPrice) { - this.FastGasPrice = FastGasPrice; + public GasOracleBuilder withFastGasPrice(Wei fastGasPrice) { + this.fastGasPrice = fastGasPrice; return this; } @@ -109,19 +117,29 @@ public GasOracleBuilder withSuggestBaseFee(Double suggestBaseFee) { return this; } - public GasOracleBuilder withGasUsedRatio(String gasUsedRatio) { + public GasOracleBuilder withGasUsedRatio(List gasUsedRatio) { this.gasUsedRatio = gasUsedRatio; return this; } public GasOracle build() { GasOracle gasOracle = new GasOracle(); - gasOracle.ProposeGasPrice = this.ProposeGasPrice; - gasOracle.LastBlock = this.LastBlock; + gasOracle.LastBlock = this.lastBlock; gasOracle.suggestBaseFee = this.suggestBaseFee; - gasOracle.SafeGasPrice = this.SafeGasPrice; - gasOracle.FastGasPrice = this.FastGasPrice; - gasOracle.gasUsedRatio = this.gasUsedRatio; + if (this.proposeGasPrice != null) { + gasOracle.ProposeGasPrice = this.proposeGasPrice.asGwei().intValue(); + } + if (this.safeGasPrice != null) { + gasOracle.SafeGasPrice = this.safeGasPrice.asGwei().intValue(); + } + if (this.fastGasPrice != null) { + gasOracle.FastGasPrice = this.fastGasPrice.asGwei().intValue(); + } + if (this.gasUsedRatio != null) { + gasOracle.gasUsedRatio = this.gasUsedRatio.stream() + .map(BigDecimal::toString) + .collect(Collectors.joining(", ")); + } return gasOracle; } } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Log.java b/src/main/java/io/goodforgod/api/etherscan/model/Log.java index bd03103..5ed840a 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Log.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Log.java @@ -37,6 +37,8 @@ public class Log { @Expose(deserialize = false, serialize = false) private Long _logIndex; + protected Log() {} + // public Long getBlockNumber() { if (_blockNumber == null && !BasicUtils.isEmpty(blockNumber)) { diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Price.java b/src/main/java/io/goodforgod/api/etherscan/model/Price.java index 9c72792..b24fc65 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Price.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Price.java @@ -19,6 +19,8 @@ public class Price { @Expose(deserialize = false, serialize = false) private LocalDateTime _ethbtc_timestamp; + protected Price() {} + public double inUsd() { return ethusd; } @@ -101,22 +103,22 @@ public static final class PriceBuilder { private PriceBuilder() {} - public PriceBuilder withEthusd(double ethusd) { + public PriceBuilder withEthUsd(double ethusd) { this.ethusd = ethusd; return this; } - public PriceBuilder withEthbtc(double ethbtc) { + public PriceBuilder withEthBtc(double ethbtc) { this.ethbtc = ethbtc; return this; } - public PriceBuilder withEthusdTimestamp(LocalDateTime ethusdTimestamp) { + public PriceBuilder withEthUsdTimestamp(LocalDateTime ethusdTimestamp) { this.ethusdTimestamp = ethusdTimestamp; return this; } - public PriceBuilder withEthbtcTimestamp(LocalDateTime ethbtcTimestamp) { + public PriceBuilder withEthBtcTimestamp(LocalDateTime ethbtcTimestamp) { this.ethbtcTimestamp = ethbtcTimestamp; return this; } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Status.java b/src/main/java/io/goodforgod/api/etherscan/model/Status.java index 8cdc704..eaf9b8a 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Status.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Status.java @@ -16,6 +16,8 @@ public class Status { private int isError; private String errDescription; + protected Status() {} + public boolean haveError() { return isError == 1; } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Tx.java b/src/main/java/io/goodforgod/api/etherscan/model/Tx.java index 65c24ba..7e09768 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Tx.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Tx.java @@ -22,6 +22,8 @@ public class Tx extends BaseTx { private String isError; private String txreceipt_status; + protected Tx() {} + // public BigInteger getValue() { return value; diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java b/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java index 84e2d40..edf578f 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java @@ -18,10 +18,12 @@ public class TxErc1155 extends BaseTx { private String tokenSymbol; private String tokenValue; private int transactionIndex; - private long gasPrice; - private long cumulativeGasUsed; + private BigInteger gasPrice; + private BigInteger cumulativeGasUsed; private long confirmations; + protected TxErc1155() {} + // public long getNonce() { return nonce; @@ -51,11 +53,11 @@ public int getTransactionIndex() { return transactionIndex; } - public long getGasPrice() { + public BigInteger getGasPrice() { return gasPrice; } - public long getCumulativeGasUsed() { + public BigInteger getCumulativeGasUsed() { return cumulativeGasUsed; } @@ -120,8 +122,8 @@ public static final class TxErc1155Builder { private String tokenSymbol; private String tokenValue; private int transactionIndex; - private long gasPrice; - private long cumulativeGasUsed; + private BigInteger gasPrice; + private BigInteger cumulativeGasUsed; private long confirmations; private TxErc1155Builder() {} @@ -206,12 +208,12 @@ public TxErc1155Builder withTransactionIndex(int transactionIndex) { return this; } - public TxErc1155Builder withGasPrice(long gasPrice) { + public TxErc1155Builder withGasPrice(BigInteger gasPrice) { this.gasPrice = gasPrice; return this; } - public TxErc1155Builder withCumulativeGasUsed(long cumulativeGasUsed) { + public TxErc1155Builder withCumulativeGasUsed(BigInteger cumulativeGasUsed) { this.cumulativeGasUsed = cumulativeGasUsed; return this; } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java b/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java index f51b855..9342c8e 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java @@ -18,10 +18,12 @@ public class TxErc20 extends BaseTx { private String tokenSymbol; private String tokenDecimal; private int transactionIndex; - private long gasPrice; - private long cumulativeGasUsed; + private BigInteger gasPrice; + private BigInteger cumulativeGasUsed; private long confirmations; + protected TxErc20() {} + // public long getNonce() { return nonce; @@ -51,11 +53,11 @@ public int getTransactionIndex() { return transactionIndex; } - public long getGasPrice() { + public BigInteger getGasPrice() { return gasPrice; } - public long getCumulativeGasUsed() { + public BigInteger getCumulativeGasUsed() { return cumulativeGasUsed; } @@ -120,8 +122,8 @@ public static final class TxERC20Builder { private String tokenSymbol; private String tokenDecimal; private int transactionIndex; - private long gasPrice; - private long cumulativeGasUsed; + private BigInteger gasPrice; + private BigInteger cumulativeGasUsed; private long confirmations; private TxERC20Builder() {} @@ -206,12 +208,12 @@ public TxERC20Builder withTransactionIndex(int transactionIndex) { return this; } - public TxERC20Builder withGasPrice(long gasPrice) { + public TxERC20Builder withGasPrice(BigInteger gasPrice) { this.gasPrice = gasPrice; return this; } - public TxERC20Builder withCumulativeGasUsed(long cumulativeGasUsed) { + public TxERC20Builder withCumulativeGasUsed(BigInteger cumulativeGasUsed) { this.cumulativeGasUsed = cumulativeGasUsed; return this; } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java b/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java index 8fb2467..1276b71 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java @@ -18,10 +18,12 @@ public class TxErc721 extends BaseTx { private String tokenSymbol; private String tokenDecimal; private int transactionIndex; - private long gasPrice; - private long cumulativeGasUsed; + private BigInteger gasPrice; + private BigInteger cumulativeGasUsed; private long confirmations; + protected TxErc721() {} + // public long getNonce() { return nonce; @@ -51,11 +53,11 @@ public int getTransactionIndex() { return transactionIndex; } - public long getGasPrice() { + public BigInteger getGasPrice() { return gasPrice; } - public long getCumulativeGasUsed() { + public BigInteger getCumulativeGasUsed() { return cumulativeGasUsed; } @@ -120,8 +122,8 @@ public static final class TxERC721Builder { private String tokenSymbol; private String tokenDecimal; private int transactionIndex; - private long gasPrice; - private long cumulativeGasUsed; + private BigInteger gasPrice; + private BigInteger cumulativeGasUsed; private long confirmations; private TxERC721Builder() {} @@ -206,12 +208,12 @@ public TxERC721Builder withTransactionIndex(int transactionIndex) { return this; } - public TxERC721Builder withGasPrice(long gasPrice) { + public TxERC721Builder withGasPrice(BigInteger gasPrice) { this.gasPrice = gasPrice; return this; } - public TxERC721Builder withCumulativeGasUsed(long cumulativeGasUsed) { + public TxERC721Builder withCumulativeGasUsed(BigInteger cumulativeGasUsed) { this.cumulativeGasUsed = cumulativeGasUsed; return this; } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java b/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java index 84e10b3..68bdebf 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java @@ -17,6 +17,8 @@ public class TxInternal extends BaseTx { private int isError; private String errCode; + protected TxInternal() {} + // public BigInteger getValue() { return value; @@ -102,7 +104,7 @@ public TxInternalBuilder withBlockNumber(long blockNumber) { return this; } - public TxInternalBuilder with_timeStamp(LocalDateTime timeStamp) { + public TxInternalBuilder withTimeStamp(LocalDateTime timeStamp) { this.timeStamp = timeStamp; return this; } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java index 0e6ff3a..1eb46f3 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java @@ -47,6 +47,8 @@ public class BlockProxy { private String transactionsRoot; private List transactions; + protected BlockProxy() {} + // public Long getNumber() { if (_number == null && !BasicUtils.isEmpty(number)) diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java index 73a21b6..a57ce36 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java @@ -34,6 +34,8 @@ public class ReceiptProxy { private List logs; private String logsBloom; + protected ReceiptProxy() {} + // public String getRoot() { return root; diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java index 52fe41b..b6324e1 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java @@ -35,6 +35,8 @@ public class TxProxy { @Expose(deserialize = false, serialize = false) private Long _blockNumber; + protected TxProxy() {} + // public String getTo() { return to; diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc1155ResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc1155ResponseTO.java new file mode 100644 index 0000000..3bf9d49 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc1155ResponseTO.java @@ -0,0 +1,11 @@ +package io.goodforgod.api.etherscan.model.response; + +import io.goodforgod.api.etherscan.model.TxErc1155; + +/** + * @author GoodforGod + * @since 14.05.2023 + */ +public class TxErc1155ResponseTO extends BaseListResponseTO { + +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc20ResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc20ResponseTO.java new file mode 100644 index 0000000..d5d3f6e --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc20ResponseTO.java @@ -0,0 +1,11 @@ +package io.goodforgod.api.etherscan.model.response; + +import io.goodforgod.api.etherscan.model.TxErc20; + +/** + * @author GoodforGod + * @since 29.10.2018 + */ +public class TxErc20ResponseTO extends BaseListResponseTO { + +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc721ResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc721ResponseTO.java new file mode 100644 index 0000000..27518ae --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/TxErc721ResponseTO.java @@ -0,0 +1,11 @@ +package io.goodforgod.api.etherscan.model.response; + +import io.goodforgod.api.etherscan.model.TxErc721; + +/** + * @author GoodforGod + * @since 14.05.2023 + */ +public class TxErc721ResponseTO extends BaseListResponseTO { + +} diff --git a/src/test/java/io/goodforgod/api/etherscan/block/BlockApiTests.java b/src/test/java/io/goodforgod/api/etherscan/block/BlockApiTests.java index 3e84ab7..7a923aa 100644 --- a/src/test/java/io/goodforgod/api/etherscan/block/BlockApiTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/block/BlockApiTests.java @@ -25,7 +25,7 @@ void correct() { assertNotEquals(-1, uncle.get().getUncles().get(0).getUnclePosition()); assertNotNull(uncle.get().toString()); - BlockUncle empty = new BlockUncle(); + BlockUncle empty = BlockUncle.builder().build(); assertNotEquals(uncle.get().hashCode(), empty.hashCode()); assertNotEquals(uncle.get(), empty); assertTrue(empty.isEmpty()); diff --git a/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java b/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java index 6f5fc22..7db6aae 100644 --- a/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java @@ -1,7 +1,270 @@ package io.goodforgod.api.etherscan.model; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.time.LocalDateTime; +import java.util.Collections; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + /** * @author Anton Kurako (GoodforGod) * @since 14.05.2023 */ -class ModelBuilderTests {} +class ModelBuilderTests extends Assertions { + + @Test + void abiBuilder() { + Abi value = Abi.builder() + .withContractAbi("1") + .withIsVerified(true) + .build(); + + assertNotNull(value); + assertTrue(value.isVerified()); + assertEquals("1", value.getContractAbi()); + } + + @Test + void blockBuilder() { + LocalDateTime timestamp = LocalDateTime.now(); + Block value = Block.builder() + .withBlockNumber(1) + .withBlockReward(BigInteger.ONE) + .withTimeStamp(timestamp) + .build(); + + assertNotNull(value); + assertEquals(1, value.getBlockNumber()); + assertEquals(BigInteger.ONE, value.getBlockReward()); + assertEquals(timestamp, value.getTimeStamp()); + } + + @Test + void blockUncleBuilder() { + LocalDateTime timestamp = LocalDateTime.now(); + BlockUncle value = BlockUncle.builder() + .withBlockNumber(1) + .withBlockReward(BigInteger.ONE) + .withTimeStamp(timestamp) + .withBlockMiner("1") + .withUncleInclusionReward("1") + .withUncles(Collections.singletonList(BlockUncle.Uncle.builder() + .withBlockreward(BigInteger.ONE) + .withMiner("1") + .withUnclePosition(1) + .build())) + .build(); + + assertNotNull(value); + assertEquals(1, value.getBlockNumber()); + assertEquals(BigInteger.ONE, value.getBlockReward()); + assertEquals(timestamp, value.getTimeStamp()); + } + + @Test + void gasOracleBuilder() { + GasOracle value = GasOracle.builder() + .withFastGasPrice(new Wei(1000000000)) + .withProposeGasPrice(new Wei(1000000000)) + .withSafeGasPrice(new Wei(1000000000)) + .withGasUsedRatio(Collections.singletonList(new BigDecimal(1))) + .withLastBlock(1L) + .withSuggestBaseFee(1.0) + .build(); + + assertNotNull(value); + assertEquals(new Wei(1000000000), value.getFastGasPriceInWei()); + } + + @Test + void logBuilder() { + LocalDateTime timestamp = LocalDateTime.now(); + Log value = Log.builder() + .withAddress("1") + .withBlockNumber(1L) + .withData("1") + .withGasPrice(BigInteger.ONE) + .withGasUsed(BigInteger.ONE) + .withLogIndex(1L) + .withTimeStamp(timestamp) + .withTransactionHash("1") + .withTransactionIndex(1L) + .withTopics(Collections.singletonList("1")) + .build(); + + assertNotNull(value); + assertEquals(1, value.getTopics().size()); + } + + @Test + void priceBuilder() { + LocalDateTime timestamp = LocalDateTime.now(); + Price value = Price.builder() + .withEthBtc(1.0) + .withEthUsd(1.0) + .withEthBtcTimestamp(timestamp) + .withEthUsdTimestamp(timestamp) + .build(); + + assertNotNull(value); + assertEquals(1.0, value.inUsd()); + assertEquals(1.0, value.inBtc()); + } + + @Test + void statusBuilder() { + Status value = Status.builder() + .withIsError(1) + .withErrDescription("1") + .build(); + + assertNotNull(value); + assertTrue(value.haveError()); + assertEquals("1", value.getErrDescription()); + } + + @Test + void txBuilder() { + LocalDateTime timestamp = LocalDateTime.now(); + Tx value = Tx.builder() + .withBlockHash("1") + .withBlockNumber(1L) + .withConfirmations(1L) + .withContractAddress("1") + .withCumulativeGasUsed(BigInteger.ONE) + .withFrom("1") + .withTo("1") + .withGas(BigInteger.ONE) + .withGasPrice(BigInteger.ONE) + .withGasUsed(BigInteger.ONE) + .withHash("1") + .withInput("1") + .withIsError("1") + .withNonce(1L) + .withTimeStamp(timestamp) + .withValue(BigInteger.ONE) + .withTransactionIndex(1) + .withTxreceiptStatus("1") + .build(); + + assertNotNull(value); + assertTrue(value.haveError()); + assertEquals("1", value.getTo()); + assertEquals("1", value.getFrom()); + } + + @Test + void txErc20Builder() { + LocalDateTime timestamp = LocalDateTime.now(); + TxErc20 value = TxErc20.builder() + .withBlockHash("1") + .withBlockNumber(1L) + .withConfirmations(1L) + .withContractAddress("1") + .withCumulativeGasUsed(BigInteger.ONE) + .withFrom("1") + .withTo("1") + .withGas(BigInteger.ONE) + .withGasPrice(BigInteger.ONE) + .withGasUsed(BigInteger.ONE) + .withHash("1") + .withInput("1") + .withTokenName("1") + .withTokenSymbol("1") + .withTokenDecimal("1") + .withNonce(1L) + .withTimeStamp(timestamp) + .withValue(BigInteger.ONE) + .withTransactionIndex(1) + .build(); + + assertNotNull(value); + assertEquals("1", value.getTo()); + assertEquals("1", value.getFrom()); + } + + @Test + void txErc721Builder() { + LocalDateTime timestamp = LocalDateTime.now(); + TxErc721 value = TxErc721.builder() + .withBlockHash("1") + .withBlockNumber(1L) + .withConfirmations(1L) + .withContractAddress("1") + .withCumulativeGasUsed(BigInteger.ONE) + .withFrom("1") + .withTo("1") + .withGas(BigInteger.ONE) + .withGasPrice(BigInteger.ONE) + .withGasUsed(BigInteger.ONE) + .withHash("1") + .withInput("1") + .withTokenName("1") + .withTokenSymbol("1") + .withTokenDecimal("1") + .withTokenID("1") + .withNonce(1L) + .withTimeStamp(timestamp) + .withTransactionIndex(1) + .build(); + + assertNotNull(value); + assertEquals("1", value.getTo()); + assertEquals("1", value.getFrom()); + } + + @Test + void txErc1155Builder() { + LocalDateTime timestamp = LocalDateTime.now(); + TxErc1155 value = TxErc1155.builder() + .withBlockHash("1") + .withBlockNumber(1L) + .withConfirmations(1L) + .withContractAddress("1") + .withCumulativeGasUsed(BigInteger.ONE) + .withFrom("1") + .withTo("1") + .withGas(BigInteger.ONE) + .withGasPrice(BigInteger.ONE) + .withGasUsed(BigInteger.ONE) + .withHash("1") + .withInput("1") + .withTokenName("1") + .withTokenSymbol("1") + .withTokenDecimal("1") + .withTokenID("1") + .withNonce(1L) + .withTimeStamp(timestamp) + .withTransactionIndex(1) + .build(); + + assertNotNull(value); + assertEquals("1", value.getTo()); + assertEquals("1", value.getFrom()); + } + + @Test + void txInternalBuilder() { + LocalDateTime timestamp = LocalDateTime.now(); + TxInternal value = TxInternal.builder() + .withBlockNumber(1L) + .withContractAddress("1") + .withFrom("1") + .withTo("1") + .withGas(BigInteger.ONE) + .withGasUsed(BigInteger.ONE) + .withHash("1") + .withInput("1") + .withTimeStamp(timestamp) + .withErrCode("1") + .withIsError(1) + .withTraceId("1") + .withType("1") + .build(); + + assertNotNull(value); + assertEquals("1", value.getTo()); + assertEquals("1", value.getFrom()); + } +} diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTests.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTests.java index 10dc6fd..874ccc0 100644 --- a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTests.java @@ -50,7 +50,7 @@ void correct() { assertNotNull(proxy.getUncles()); assertNotNull(proxy.toString()); - BlockProxy empty = new BlockProxy(); + BlockProxy empty = BlockProxy.builder().build(); assertNotEquals(proxy, empty); assertNotEquals(proxy.hashCode(), empty.hashCode()); } diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxApiTests.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxApiTests.java index 6c7dbb7..b20369e 100644 --- a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxApiTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxApiTests.java @@ -24,7 +24,7 @@ void correctByHash() { assertNotNull(tx.get().getBlockNumber()); assertNotNull(tx.get().toString()); - TxProxy empty = new TxProxy(); + TxProxy empty = TxProxy.builder().build(); assertNotEquals(tx.get(), empty); assertNotEquals(tx.get().hashCode(), empty.hashCode()); } diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxReceiptApiTests.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxReceiptApiTests.java index 7a6624c..e4322f2 100644 --- a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxReceiptApiTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxReceiptApiTests.java @@ -32,7 +32,7 @@ void correct() { assertNull(infoProxy.get().getContractAddress()); assertNotNull(infoProxy.get().toString()); - ReceiptProxy empty = new ReceiptProxy(); + ReceiptProxy empty = ReceiptProxy.builder().build(); assertNotEquals(empty, infoProxy.get()); assertNotEquals(empty.hashCode(), infoProxy.get().hashCode()); } diff --git a/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTests.java b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTests.java index 37e0ec0..3525e21 100644 --- a/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTests.java @@ -20,7 +20,7 @@ void correct() { assertNotEquals(0.0, price.inUsd()); assertNotNull(price.toString()); - Price empty = new Price(); + Price empty = Price.builder().build(); assertNotEquals(price, empty); assertNotEquals(price.hashCode(), empty.hashCode()); } diff --git a/src/test/java/io/goodforgod/api/etherscan/transaction/TransactionExecApiTests.java b/src/test/java/io/goodforgod/api/etherscan/transaction/TransactionExecApiTests.java index eb595c3..23e512c 100644 --- a/src/test/java/io/goodforgod/api/etherscan/transaction/TransactionExecApiTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/transaction/TransactionExecApiTests.java @@ -20,7 +20,7 @@ void correct() { assertNotNull(status.get().getErrDescription()); assertNotNull(status.get().toString()); - Status empty = new Status(); + Status empty = Status.builder().build(); assertNotEquals(empty, status.get()); assertNotEquals(empty.hashCode(), status.get().hashCode()); } From aa251291f27a11347fc7f63fdbcb2ecf6c4c4de5 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 14 May 2023 12:52:59 +0300 Subject: [PATCH 29/61] [2.0.0-SNAPSHOT] CI key env fixed --- .github/workflows/gradle.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml index 3eb55f0..31c42f0 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/gradle.yml @@ -35,13 +35,13 @@ jobs: if: matrix.java == '11' run: ./gradlew test jacocoTestReport env: - API_KEY: ${{ secrets.ETHERSCAN_API_KEY_1 }} + ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY_1 }} - name: Test if: matrix.java == '17' run: ./gradlew test jacocoTestReport env: - API_KEY: ${{ secrets.ETHERSCAN_API_KEY_2 }} + ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY_2 }} - name: SonarQube if: matrix.java == '17' From 873f5828e525e109190b409295b0ef507a68f053 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 14 May 2023 13:04:56 +0300 Subject: [PATCH 30/61] [2.0.0-SNAPSHOT] Builders NPE fixes Hashcode & Equals improved --- .../goodforgod/api/etherscan/model/Abi.java | 19 ++----- .../api/etherscan/model/Balance.java | 14 ++--- .../goodforgod/api/etherscan/model/Block.java | 13 ++--- .../api/etherscan/model/BlockUncle.java | 51 ++++--------------- .../goodforgod/api/etherscan/model/Log.java | 51 +++++++------------ .../goodforgod/api/etherscan/model/Price.java | 51 +++++++------------ .../io/goodforgod/api/etherscan/model/Tx.java | 6 ++- .../api/etherscan/model/TxErc1155.java | 6 ++- .../api/etherscan/model/TxErc20.java | 6 ++- .../api/etherscan/model/TxErc721.java | 6 ++- .../api/etherscan/model/TxInternal.java | 6 ++- .../goodforgod/api/etherscan/model/Wei.java | 7 +-- .../api/etherscan/model/proxy/BlockProxy.java | 18 ++++--- .../etherscan/model/proxy/ReceiptProxy.java | 12 +++-- .../api/etherscan/model/proxy/TxProxy.java | 12 +++-- 15 files changed, 108 insertions(+), 170 deletions(-) diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Abi.java b/src/main/java/io/goodforgod/api/etherscan/model/Abi.java index 3fce40a..3536bf9 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Abi.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Abi.java @@ -1,6 +1,7 @@ package io.goodforgod.api.etherscan.model; import io.goodforgod.api.etherscan.util.BasicUtils; +import java.util.Objects; /** * @author GoodforGod @@ -40,27 +41,15 @@ public boolean isVerified() { public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) + if (!(o instanceof Abi)) return false; - Abi abi = (Abi) o; - - if (isVerified != abi.isVerified) - return false; - return contractAbi != null - ? contractAbi.equals(abi.contractAbi) - : abi.contractAbi == null; + return isVerified == abi.isVerified && Objects.equals(contractAbi, abi.contractAbi); } @Override public int hashCode() { - int result = contractAbi != null - ? contractAbi.hashCode() - : 0; - result = 31 * result + (isVerified - ? 1 - : 0); - return result; + return Objects.hash(contractAbi, isVerified); } @Override diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Balance.java b/src/main/java/io/goodforgod/api/etherscan/model/Balance.java index 38379e6..4de8a54 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Balance.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Balance.java @@ -48,23 +48,15 @@ public BigInteger getEther() { public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) + if (!(o instanceof Balance)) return false; - Balance balance1 = (Balance) o; - - if (!balance.equals(balance1.balance)) - return false; - return Objects.equals(address, balance1.address); + return Objects.equals(balance, balance1.balance) && Objects.equals(address, balance1.address); } @Override public int hashCode() { - int result = balance.hashCode(); - result = 31 * result + (address != null - ? address.hashCode() - : 0); - return result; + return Objects.hash(balance, address); } @Override diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Block.java b/src/main/java/io/goodforgod/api/etherscan/model/Block.java index d46fb44..95bfbcb 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Block.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Block.java @@ -5,6 +5,7 @@ import java.math.BigInteger; import java.time.LocalDateTime; import java.time.ZoneOffset; +import java.util.Objects; /** * @author GoodforGod @@ -40,17 +41,15 @@ public BigInteger getBlockReward() { public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) + if (!(o instanceof Block)) return false; - Block block = (Block) o; - return blockNumber == block.blockNumber; } @Override public int hashCode() { - return (int) (blockNumber ^ (blockNumber >>> 32)); + return Objects.hash(blockNumber); } @Override @@ -98,8 +97,10 @@ public Block build() { Block block = new Block(); block.blockNumber = this.blockNumber; block.blockReward = this.blockReward; - block._timeStamp = this.timeStamp; - block.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + if (this.timeStamp != null) { + block._timeStamp = this.timeStamp; + block.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + } return block; } } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java b/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java index 02ddc28..9b110d9 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java @@ -5,6 +5,7 @@ import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.List; +import java.util.Objects; /** * @author GoodforGod @@ -38,31 +39,16 @@ public int getUnclePosition() { public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) + if (!(o instanceof Uncle)) return false; - Uncle uncle = (Uncle) o; - if (unclePosition != uncle.unclePosition) - return false; - if (miner != null - ? !miner.equals(uncle.miner) - : uncle.miner != null) - return false; - return blockreward != null - ? blockreward.equals(uncle.blockreward) - : uncle.blockreward == null; + return unclePosition == uncle.unclePosition && Objects.equals(miner, uncle.miner) + && Objects.equals(blockreward, uncle.blockreward); } @Override public int hashCode() { - int result = miner != null - ? miner.hashCode() - : 0; - result = 31 * result + (blockreward != null - ? blockreward.hashCode() - : 0); - result = 31 * result + unclePosition; - return result; + return Objects.hash(miner, blockreward, unclePosition); } @Override @@ -139,27 +125,6 @@ public String getUncleInclusionReward() { } // - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - if (!super.equals(o)) - return false; - - BlockUncle that = (BlockUncle) o; - - return getBlockNumber() != 0 && getBlockNumber() == that.getBlockNumber(); - } - - @Override - public int hashCode() { - int result = super.hashCode(); - result = (int) (31 * result + getBlockNumber()); - return result; - } - @Override public String toString() { return "UncleBlock{" + @@ -223,8 +188,10 @@ public BlockUncle build() { blockUncle.blockNumber = this.blockNumber; blockUncle.blockReward = this.blockReward; blockUncle.blockMiner = this.blockMiner; - blockUncle._timeStamp = this.timeStamp; - blockUncle.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + if (this.timeStamp != null) { + blockUncle._timeStamp = this.timeStamp; + blockUncle.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + } return blockUncle; } } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Log.java b/src/main/java/io/goodforgod/api/etherscan/model/Log.java index 5ed840a..07e652f 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Log.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Log.java @@ -125,40 +125,17 @@ public Long getLogIndex() { public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) + if (!(o instanceof Log)) return false; - Log log = (Log) o; - - if (!Objects.equals(blockNumber, log.blockNumber)) - return false; - if (!Objects.equals(address, log.address)) - return false; - if (!Objects.equals(transactionHash, log.transactionHash)) - return false; - if (!Objects.equals(timeStamp, log.timeStamp)) - return false; - return Objects.equals(logIndex, log.logIndex); + return Objects.equals(blockNumber, log.blockNumber) && Objects.equals(address, log.address) + && Objects.equals(transactionHash, log.transactionHash) && Objects.equals(transactionIndex, log.transactionIndex) + && Objects.equals(logIndex, log.logIndex); } @Override public int hashCode() { - int result = blockNumber != null - ? blockNumber.hashCode() - : 0; - result = 31 * result + (address != null - ? address.hashCode() - : 0); - result = 31 * result + (transactionHash != null - ? transactionHash.hashCode() - : 0); - result = 31 * result + (timeStamp != null - ? timeStamp.hashCode() - : 0); - result = 31 * result + (logIndex != null - ? logIndex.hashCode() - : 0); - return result; + return Objects.hash(blockNumber, address, transactionHash, transactionIndex, logIndex); } @Override @@ -255,17 +232,23 @@ public LogBuilder withLogIndex(Long logIndex) { public Log build() { Log log = new Log(); log.address = this.address; - log.gasPrice = String.valueOf(this.gasPrice); - log._gasPrice = this.gasPrice; + if (this.gasPrice != null) { + log.gasPrice = String.valueOf(this.gasPrice); + log._gasPrice = this.gasPrice; + } log._logIndex = this.logIndex; log._transactionIndex = this.transactionIndex; - log._gasUsed = this.gasUsed; log.blockNumber = String.valueOf(this.blockNumber); log.transactionIndex = String.valueOf(this.transactionIndex); - log.timeStamp = String.valueOf(this.timeStamp); + if (this.timeStamp != null) { + log.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + log._timeStamp = this.timeStamp; + } log.data = this.data; - log.gasUsed = String.valueOf(this.gasUsed); - log._timeStamp = this.timeStamp; + if (this.gasUsed != null) { + log.gasUsed = String.valueOf(this.gasUsed); + log._gasUsed = this.gasUsed; + } log.logIndex = String.valueOf(this.logIndex); log._blockNumber = this.blockNumber; log.topics = this.topics; diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Price.java b/src/main/java/io/goodforgod/api/etherscan/model/Price.java index b24fc65..4ef4491 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Price.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Price.java @@ -3,6 +3,7 @@ import com.google.gson.annotations.Expose; import java.time.LocalDateTime; import java.time.ZoneOffset; +import java.util.Objects; /** * @author GoodforGod @@ -30,14 +31,16 @@ public double inBtc() { } public LocalDateTime usdTimestamp() { - if (_ethusd_timestamp == null) + if (_ethusd_timestamp == null && ethusd_timestamp != null) { _ethusd_timestamp = LocalDateTime.ofEpochSecond(Long.parseLong(ethusd_timestamp), 0, ZoneOffset.UTC); + } return _ethusd_timestamp; } public LocalDateTime btcTimestamp() { - if (_ethbtc_timestamp == null) + if (_ethbtc_timestamp == null && ethbtc_timestamp != null) { _ethbtc_timestamp = LocalDateTime.ofEpochSecond(Long.parseLong(ethbtc_timestamp), 0, ZoneOffset.UTC); + } return _ethbtc_timestamp; } @@ -45,39 +48,17 @@ public LocalDateTime btcTimestamp() { public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) + if (!(o instanceof Price)) return false; - Price price = (Price) o; - - if (Double.compare(price.ethusd, ethusd) != 0) - return false; - if (Double.compare(price.ethbtc, ethbtc) != 0) - return false; - if (ethusd_timestamp != null - ? !ethusd_timestamp.equals(price.ethusd_timestamp) - : price.ethusd_timestamp != null) - return false; - return (ethbtc_timestamp != null - ? !ethbtc_timestamp.equals(price.ethbtc_timestamp) - : price.ethbtc_timestamp != null); + return Double.compare(price.ethusd, ethusd) == 0 && Double.compare(price.ethbtc, ethbtc) == 0 + && Objects.equals(ethusd_timestamp, price.ethusd_timestamp) + && Objects.equals(ethbtc_timestamp, price.ethbtc_timestamp); } @Override public int hashCode() { - int result; - long temp; - temp = Double.doubleToLongBits(ethusd); - result = (int) (temp ^ (temp >>> 32)); - temp = Double.doubleToLongBits(ethbtc); - result = 31 * result + (int) (temp ^ (temp >>> 32)); - result = 31 * result + (ethusd_timestamp != null - ? ethusd_timestamp.hashCode() - : 0); - result = 31 * result + (ethbtc_timestamp != null - ? ethbtc_timestamp.hashCode() - : 0); - return result; + return Objects.hash(ethusd, ethbtc, ethusd_timestamp, ethbtc_timestamp); } @Override @@ -126,11 +107,15 @@ public PriceBuilder withEthBtcTimestamp(LocalDateTime ethbtcTimestamp) { public Price build() { Price price = new Price(); price.ethbtc = this.ethbtc; - price.ethbtc_timestamp = String.valueOf(this.ethbtcTimestamp.toEpochSecond(ZoneOffset.UTC)); - price._ethbtc_timestamp = this.ethbtcTimestamp; price.ethusd = this.ethusd; - price.ethusd_timestamp = String.valueOf(this.ethusdTimestamp.toEpochSecond(ZoneOffset.UTC)); - price._ethusd_timestamp = this.ethusdTimestamp; + if (this.ethbtcTimestamp != null) { + price.ethbtc_timestamp = String.valueOf(this.ethbtcTimestamp.toEpochSecond(ZoneOffset.UTC)); + price._ethbtc_timestamp = this.ethbtcTimestamp; + } + if (this.ethusdTimestamp != null) { + price.ethusd_timestamp = String.valueOf(this.ethusdTimestamp.toEpochSecond(ZoneOffset.UTC)); + price._ethusd_timestamp = this.ethusdTimestamp; + } return price; } } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Tx.java b/src/main/java/io/goodforgod/api/etherscan/model/Tx.java index 7e09768..3d8cd1f 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Tx.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Tx.java @@ -228,10 +228,12 @@ public Tx build() { tx.value = this.value; tx.transactionIndex = this.transactionIndex; tx.confirmations = this.confirmations; - tx.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + if (this.timeStamp != null) { + tx.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + tx._timeStamp = this.timeStamp; + } tx.nonce = this.nonce; tx.blockNumber = this.blockNumber; - tx._timeStamp = this.timeStamp; tx.to = this.to; tx.input = this.input; tx.cumulativeGasUsed = this.cumulativeGasUsed; diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java b/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java index edf578f..e6c20f0 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java @@ -235,9 +235,11 @@ public TxErc1155 build() { txERC721.contractAddress = this.contractAddress; txERC721.cumulativeGasUsed = this.cumulativeGasUsed; txERC721.tokenID = this.tokenID; - txERC721.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + if (this.timeStamp != null) { + txERC721.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + txERC721._timeStamp = this.timeStamp; + } txERC721.blockNumber = this.blockNumber; - txERC721._timeStamp = this.timeStamp; txERC721.tokenValue = this.tokenValue; txERC721.transactionIndex = this.transactionIndex; txERC721.to = this.to; diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java b/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java index 9342c8e..197ab5d 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java @@ -237,10 +237,12 @@ public TxErc20 build() { txERC20.nonce = this.nonce; txERC20.confirmations = this.confirmations; txERC20.value = this.value; - txERC20.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + if (this.timeStamp != null) { + txERC20.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + txERC20._timeStamp = this.timeStamp; + } txERC20.blockHash = this.blockHash; txERC20.blockNumber = this.blockNumber; - txERC20._timeStamp = this.timeStamp; txERC20.gasPrice = this.gasPrice; txERC20.to = this.to; txERC20.input = this.input; diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java b/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java index 1276b71..644f738 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java @@ -235,9 +235,11 @@ public TxErc721 build() { txERC721.contractAddress = this.contractAddress; txERC721.cumulativeGasUsed = this.cumulativeGasUsed; txERC721.tokenID = this.tokenID; - txERC721.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + if (this.timeStamp != null) { + txERC721.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + txERC721._timeStamp = this.timeStamp; + } txERC721.blockNumber = this.blockNumber; - txERC721._timeStamp = this.timeStamp; txERC721.tokenDecimal = this.tokenDecimal; txERC721.transactionIndex = this.transactionIndex; txERC721.to = this.to; diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java b/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java index 68bdebf..fdd89ee 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java @@ -179,10 +179,12 @@ public TxInternal build() { txInternal.from = this.from; txInternal.contractAddress = this.contractAddress; txInternal.value = this.value; - txInternal.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + if (this.timeStamp != null) { + txInternal.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + txInternal._timeStamp = this.timeStamp; + } txInternal.errCode = this.errCode; txInternal.blockNumber = this.blockNumber; - txInternal._timeStamp = this.timeStamp; txInternal.isError = this.isError; txInternal.to = this.to; txInternal.input = this.input; diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Wei.java b/src/main/java/io/goodforgod/api/etherscan/model/Wei.java index e863b7a..2fc2014 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Wei.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Wei.java @@ -45,18 +45,15 @@ public BigInteger asEther() { public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) + if (!(o instanceof Wei)) return false; - Wei wei = (Wei) o; return Objects.equals(result, wei.result); } @Override public int hashCode() { - return result != null - ? result.hashCode() - : 0; + return Objects.hash(result); } @Override diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java index 1eb46f3..a9447ca 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java @@ -342,26 +342,32 @@ public BlockProxy build() { blockProxy.mixHash = this.mixHash; blockProxy.totalDifficulty = this.totalDifficulty; blockProxy.nonce = this.nonce; - blockProxy._gasUsed = this.gasUsed; blockProxy.uncles = this.uncles; blockProxy.transactionsRoot = this.transactionsRoot; blockProxy.number = String.valueOf(this.number); blockProxy.logsBloom = this.logsBloom; blockProxy.receiptsRoot = this.receiptsRoot; - blockProxy._gasLimit = this.gasLimit; blockProxy.hash = this.hash; blockProxy.parentHash = this.parentHash; blockProxy._size = this.size; - blockProxy.gasLimit = String.valueOf(this.gasLimit); blockProxy.difficulty = this.difficulty; - blockProxy.gasUsed = String.valueOf(this.gasUsed); + if (this.gasLimit != null) { + blockProxy.gasLimit = String.valueOf(this.gasLimit); + blockProxy._gasLimit = this.gasLimit; + } + if (this.gasUsed != null) { + blockProxy.gasUsed = String.valueOf(this.gasUsed); + blockProxy._gasUsed = this.gasUsed; + } blockProxy.size = String.valueOf(this.size); blockProxy.extraData = this.extraData; blockProxy.stateRoot = this.stateRoot; - blockProxy._timestamp = this.timestamp; blockProxy.sha3Uncles = this.sha3Uncles; blockProxy.miner = this.miner; - blockProxy.timestamp = String.valueOf(this.timestamp.toEpochSecond(ZoneOffset.UTC)); + if (this.timestamp != null) { + blockProxy.timestamp = String.valueOf(this.timestamp.toEpochSecond(ZoneOffset.UTC)); + blockProxy._timestamp = this.timestamp; + } blockProxy.transactions = this.transactions; blockProxy._number = this.number; return blockProxy; diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java index a57ce36..61a7942 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java @@ -243,14 +243,18 @@ public ReceiptProxy build() { receiptProxy.blockHash = this.blockHash; receiptProxy.root = this.root; receiptProxy.contractAddress = this.contractAddress; - receiptProxy.gasUsed = String.valueOf(this.gasUsed); - receiptProxy._gasUsed = this.gasUsed; + if (this.gasUsed != null) { + receiptProxy.gasUsed = String.valueOf(this.gasUsed); + receiptProxy._gasUsed = this.gasUsed; + } receiptProxy.logs = this.logs; - receiptProxy.cumulativeGasUsed = String.valueOf(this.cumulativeGasUsed); receiptProxy.to = this.to; + if (this.cumulativeGasUsed != null) { + receiptProxy.cumulativeGasUsed = String.valueOf(this.cumulativeGasUsed); + receiptProxy._cumulativeGasUsed = this.cumulativeGasUsed; + } receiptProxy.transactionIndex = String.valueOf(this.transactionIndex); receiptProxy._blockNumber = this.blockNumber; - receiptProxy._cumulativeGasUsed = this.cumulativeGasUsed; return receiptProxy; } } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java index b6324e1..0ca7f3a 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java @@ -262,8 +262,10 @@ public TxProxyBuilder withBlockNumber(Long blockNumber) { public TxProxy build() { TxProxy txProxy = new TxProxy(); txProxy.input = this.input; - txProxy.gas = String.valueOf(this.gas); - txProxy._gas = this.gas; + if (this.gas != null) { + txProxy.gas = String.valueOf(this.gas); + txProxy._gas = this.gas; + } txProxy.s = this.s; txProxy.blockHash = this.blockHash; txProxy.to = this.to; @@ -274,12 +276,14 @@ public TxProxy build() { txProxy.v = this.v; txProxy.from = this.from; txProxy.nonce = String.valueOf(this.nonce); - txProxy._gasPrice = this.gasPrice; txProxy._transactionIndex = this.transactionIndex; txProxy.blockNumber = String.valueOf(this.blockNumber); txProxy._blockNumber = this.blockNumber; txProxy.hash = this.hash; - txProxy.gasPrice = String.valueOf(this.gasPrice); + if (this.gasPrice != null) { + txProxy.gasPrice = String.valueOf(this.gasPrice); + txProxy._gasPrice = this.gasPrice; + } return txProxy; } } From 1beaafdd691fb5ecfb7be0c0c87e9cd0690246df Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 14 May 2023 13:13:59 +0300 Subject: [PATCH 31/61] [2.0.0-SNAPSHOT] Balance contract improved Wei contract improved Supply constructor added ProxyAPI contract refactored to Wei --- .../api/etherscan/GasTrackerAPIProvider.java | 2 +- .../io/goodforgod/api/etherscan/ProxyAPI.java | 8 ++++---- .../api/etherscan/ProxyAPIProvider.java | 16 +++++++-------- .../manager/RequestQueueManager.java | 4 ++-- .../api/etherscan/model/Balance.java | 20 ++----------------- .../api/etherscan/model/Supply.java | 4 ++++ .../goodforgod/api/etherscan/model/Wei.java | 2 +- .../account/AccountBalanceListTests.java | 10 +++------- .../api/etherscan/proxy/ProxyGasApiTests.java | 14 ++++++------- .../statistic/StatisticSupplyApiTests.java | 2 +- 10 files changed, 33 insertions(+), 49 deletions(-) diff --git a/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java index a4db5ae..0b559d8 100644 --- a/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java @@ -34,7 +34,7 @@ final class GasTrackerAPIProvider extends BasicProvider implements GasTrackerAPI @Override public @NotNull GasEstimate estimate(@NotNull Wei wei) throws EtherScanException { - final String urlParams = ACT_GAS_ESTIMATE_PARAM + GASPRICE_PARAM + wei.getValue().toString(); + final String urlParams = ACT_GAS_ESTIMATE_PARAM + GASPRICE_PARAM + wei.asWei().toString(); final GasEstimateResponseTO response = getRequest(urlParams, GasEstimateResponseTO.class); if (response.getStatus() != 1) throw new EtherScanResponseException(response); diff --git a/src/main/java/io/goodforgod/api/etherscan/ProxyAPI.java b/src/main/java/io/goodforgod/api/etherscan/ProxyAPI.java index 0785d13..b379290 100644 --- a/src/main/java/io/goodforgod/api/etherscan/ProxyAPI.java +++ b/src/main/java/io/goodforgod/api/etherscan/ProxyAPI.java @@ -1,10 +1,10 @@ package io.goodforgod.api.etherscan; import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.model.Wei; import io.goodforgod.api.etherscan.model.proxy.BlockProxy; import io.goodforgod.api.etherscan.model.proxy.ReceiptProxy; import io.goodforgod.api.etherscan.model.proxy.TxProxy; -import java.math.BigInteger; import java.util.Optional; import org.jetbrains.annotations.ApiStatus.Experimental; import org.jetbrains.annotations.NotNull; @@ -150,7 +150,7 @@ public interface ProxyAPI { * @throws EtherScanException parent exception class */ @NotNull - BigInteger gasPrice() throws EtherScanException; + Wei gasPrice() throws EtherScanException; /** * Makes a call or transaction, which won't be added to the blockchain and returns the used gas, @@ -161,8 +161,8 @@ public interface ProxyAPI { * @throws EtherScanException parent exception class */ @NotNull - BigInteger gasEstimated(String hexData) throws EtherScanException; + Wei gasEstimated(String hexData) throws EtherScanException; @NotNull - BigInteger gasEstimated() throws EtherScanException; + Wei gasEstimated() throws EtherScanException; } diff --git a/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java index a306541..27e00df 100644 --- a/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java @@ -5,6 +5,7 @@ import io.goodforgod.api.etherscan.error.EtherScanResponseException; import io.goodforgod.api.etherscan.http.EthHttpClient; import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import io.goodforgod.api.etherscan.model.Wei; import io.goodforgod.api.etherscan.model.proxy.BlockProxy; import io.goodforgod.api.etherscan.model.proxy.ReceiptProxy; import io.goodforgod.api.etherscan.model.proxy.TxProxy; @@ -13,7 +14,6 @@ import io.goodforgod.api.etherscan.model.proxy.utility.TxInfoProxyTO; import io.goodforgod.api.etherscan.model.proxy.utility.TxProxyTO; import io.goodforgod.api.etherscan.util.BasicUtils; -import java.math.BigInteger; import java.util.Optional; import java.util.regex.Pattern; import org.jetbrains.annotations.NotNull; @@ -197,29 +197,29 @@ public Optional storageAt(String address, long position) throws EtherSca @NotNull @Override - public BigInteger gasPrice() throws EtherScanException { + public Wei gasPrice() throws EtherScanException { final StringProxyTO response = getRequest(ACT_GASPRICE_PARAM, StringProxyTO.class); return (BasicUtils.isEmpty(response.getResult())) - ? BigInteger.valueOf(-1) - : BasicUtils.parseHex(response.getResult()); + ? new Wei(0) + : new Wei(BasicUtils.parseHex(response.getResult())); } @NotNull @Override - public BigInteger gasEstimated() throws EtherScanException { + public Wei gasEstimated() throws EtherScanException { return gasEstimated("606060405260728060106000396000f360606040526000"); } @NotNull @Override - public BigInteger gasEstimated(String hexData) throws EtherScanException { + public Wei gasEstimated(String hexData) throws EtherScanException { if (!BasicUtils.isEmpty(hexData) && BasicUtils.isNotHex(hexData)) throw new EtherScanInvalidDataHexException("Data is not in hex format."); final String urlParams = ACT_ESTIMATEGAS_PARAM + DATA_PARAM + hexData + GAS_PARAM + "2000000000000000"; final StringProxyTO response = getRequest(urlParams, StringProxyTO.class); return (BasicUtils.isEmpty(response.getResult())) - ? BigInteger.valueOf(-1) - : BasicUtils.parseHex(response.getResult()); + ? new Wei(0) + : new Wei(BasicUtils.parseHex(response.getResult())); } } diff --git a/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java b/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java index f19603f..46a76e2 100644 --- a/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java +++ b/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java @@ -16,13 +16,13 @@ public interface RequestQueueManager extends AutoCloseable { /** * Is used by default when no API KEY is provided */ - RequestQueueManager ANONYMOUS = new SemaphoreRequestQueueManager(1, Duration.ofMillis(5005L)); + RequestQueueManager ANONYMOUS = new SemaphoreRequestQueueManager(1, Duration.ofMillis(5010L)); /** * Is available for all registered free API KEYs * Free API KEY */ - RequestQueueManager FREE_PLAN = new SemaphoreRequestQueueManager(5, Duration.ofMillis(1005L)); + RequestQueueManager FREE_PLAN = new SemaphoreRequestQueueManager(5, Duration.ofMillis(1010L)); RequestQueueManager UNLIMITED = new FakeRequestQueueManager(); diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Balance.java b/src/main/java/io/goodforgod/api/etherscan/model/Balance.java index 4de8a54..783b7d8 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Balance.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Balance.java @@ -23,24 +23,8 @@ public String getAddress() { return address; } - public BigInteger getWei() { - return balance.getValue(); - } - - public BigInteger getKwei() { - return balance.asKwei(); - } - - public BigInteger getMwei() { - return balance.asMwei(); - } - - public BigInteger getGwei() { - return balance.asGwei(); - } - - public BigInteger getEther() { - return balance.asEther(); + public Wei getBalanceInWei() { + return balance; } // diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Supply.java b/src/main/java/io/goodforgod/api/etherscan/model/Supply.java index 80dc7d0..43e3a3f 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Supply.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Supply.java @@ -8,6 +8,10 @@ */ public class Supply extends Wei { + public Supply(long value) { + super(value); + } + public Supply(BigInteger value) { super(value); } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Wei.java b/src/main/java/io/goodforgod/api/etherscan/model/Wei.java index 2fc2014..cb136df 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Wei.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Wei.java @@ -20,7 +20,7 @@ public Wei(BigInteger value) { } // - public BigInteger getValue() { + public BigInteger asWei() { return result; } diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceListTests.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceListTests.java index f611b62..0054a84 100644 --- a/src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceListTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceListTests.java @@ -29,13 +29,9 @@ void correct() { assertNotEquals(balances.get(0).hashCode(), balances.get(1).hashCode()); for (Balance balance : balances) { assertNotNull(balance.getAddress()); - assertNotNull(balance.getGwei()); - assertNotNull(balance.getKwei()); - assertNotNull(balance.getMwei()); - assertNotNull(balance.getEther()); - assertNotNull(balance.getGwei()); + assertNotNull(balance.getBalanceInWei()); assertNotNull(balance.getAddress()); - assertNotEquals(BigInteger.ZERO, balance.getWei()); + assertNotEquals(BigInteger.ZERO, balance.getBalanceInWei().asWei()); assertNotNull(balance.toString()); } } @@ -84,7 +80,7 @@ void correctParamWithEmptyExpectedResult() { assertEquals(2, balances.size()); for (Balance balance : balances) { assertNotNull(balance.getAddress()); - assertEquals(0, balance.getWei().intValue()); + assertEquals(0, balance.getBalanceInWei().asWei().intValue()); } } } diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyGasApiTests.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyGasApiTests.java index 0ab2a77..4dea82e 100644 --- a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyGasApiTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyGasApiTests.java @@ -2,7 +2,7 @@ import io.goodforgod.api.etherscan.ApiRunner; import io.goodforgod.api.etherscan.error.EtherScanInvalidDataHexException; -import java.math.BigInteger; +import io.goodforgod.api.etherscan.model.Wei; import org.junit.jupiter.api.Test; /** @@ -13,23 +13,23 @@ class ProxyGasApiTests extends ApiRunner { @Test void correctPrice() { - BigInteger price = getApi().proxy().gasPrice(); + Wei price = getApi().proxy().gasPrice(); assertNotNull(price); - assertNotEquals(0, price.intValue()); + assertNotEquals(0, price.asWei().intValue()); } @Test void correctEstimated() { - BigInteger price = getApi().proxy().gasEstimated(); + Wei price = getApi().proxy().gasEstimated(); assertNotNull(price); - assertNotEquals(0, price.intValue()); + assertNotEquals(0, price.asWei().intValue()); } @Test void correctEstimatedWithData() { String dataCustom = "606060405260728060106000396000f360606040526000606060405260728060106000396000f360606040526000"; - BigInteger price = getApi().proxy().gasEstimated(); - BigInteger priceCustom = getApi().proxy().gasEstimated(dataCustom); + Wei price = getApi().proxy().gasEstimated(); + Wei priceCustom = getApi().proxy().gasEstimated(dataCustom); assertNotNull(price); assertNotNull(priceCustom); assertNotEquals(price, priceCustom); diff --git a/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyApiTests.java b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyApiTests.java index fa79028..56469a9 100644 --- a/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyApiTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyApiTests.java @@ -15,7 +15,7 @@ class StatisticSupplyApiTests extends ApiRunner { void correct() { Supply supply = getApi().stats().supply(); assertNotNull(supply); - assertNotNull(supply.getValue()); + assertNotNull(supply.asWei()); assertNotNull(supply.asGwei()); assertNotNull(supply.asKwei()); assertNotNull(supply.asMwei()); From 948a6f3e7cd5b7aa0d6121c113035321dd059663 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 14 May 2023 21:15:56 +0300 Subject: [PATCH 32/61] [2.0.0-SNAPSHOT] README.md updated Balance & TokenBalance constructor improved --- README.md | 111 +++++++++--------- .../api/etherscan/AccountAPIProvider.java | 6 +- .../api/etherscan/EtherScanAPI.java | 5 + .../api/etherscan/model/Balance.java | 5 +- .../api/etherscan/model/TokenBalance.java | 3 +- .../account/AccountBalanceTests.java | 10 +- .../account/AccountTokenBalanceTests.java | 8 +- 7 files changed, 73 insertions(+), 75 deletions(-) diff --git a/README.md b/README.md index cd981ca..4cff68d 100644 --- a/README.md +++ b/README.md @@ -43,82 +43,84 @@ implementation "com.github.goodforgod:java-etherscan-api:2.0.0-SNAPSHOT" ## Mainnet and Testnets -API support Ethereum: *[MAINNET](https://etherscan.io), - [ROPSTEN](https://ropsten.etherscan.io), - [KOVAN](https://kovan.etherscan.io), - [RINKEBY](https://rinkeby.etherscan.io), - [GORLI](https://goerli.etherscan.io), - [TOBALABA](https://tobalaba.etherscan.com)* networks. +API support Ethereum [default networks](https://docs.etherscan.io/getting-started/endpoint-urls): +- [Mainnet](https://api.etherscan.io/) +- [Goerli](https://api-goerli.etherscan.io/) +- [Sepolia](https://api-sepolia.etherscan.io/) + ```java -EtherScanApi api = new EtherScanApi(EthNetwork.MAINNET); // Default -EtherScanApi apiRinkeby = new EtherScanApi(EthNetwork.RINKEBY); -EtherScanApi apiRopsten = new EtherScanApi(EthNetwork.ROPSTEN); -EtherScanApi apiKovan = new EtherScanApi("YourApiKey", EthNetwork.KOVAN); +EtherScanAPI api = EtherScanAPI.build(); +EtherScanAPI apiGoerli = EtherScanAPI.builder().withNetwork(EthNetworks.GORLI).build(); +EtherScanAPI apiSepolia = EtherScanAPI.builder().withNetwork(EthNetworks.SEPOLIA).build(); +``` + +### Custom Network + +In case you want to use API for other EtherScan compatible network, you can easily provide custom network with domain api URI. + +```java +EtherScanAPI api = EtherScanAPI.builder() + .withNetwork(() -> URI.create("https://api-my-custom.etherscan.io/api")) + .build(); ``` ## Custom HttpClient In case you need to set custom timeout, custom headers or better implementation for HttpClient, -just implement **IHttpExecutor** by your self or initialize it with your values. +just implement **EthHttpClient** by your self or initialize it with your values. ```java -int connectionTimeout = 10000; -int readTimeout = 7000; - -Supplier supplier = () -> new HttpExecutor(connectionTimeout); -Supplier supplierFull = () -> new HttpExecutor(connectionTimeout, readTimeout); - -EtherScanApi api = new EtherScanApi(EthNetwork.RINKEBY, supplier); -EtherScanApi apiWithKey = new EtherScanApi("YourApiKey", EthNetwork.MAINNET, supplierFull); +Supplier ethHttpClientSupplier = () -> new UrlEthHttpClient(Duration.ofMillis(300), Duration.ofMillis(300)); +EtherScanAPI api = EtherScanAPI.builder() + .withHttpClient(supplier) + .build(); ``` ## API Examples -You can read about all API methods on [Etherscan](https://etherscan.io/apis) +You can read about all API methods on [Etherscan](https://docs.etherscan.io/api-endpoints/accounts) *Library support all available EtherScan API.* -You can use library *with or without* API key *([Check API request\sec restrictions when used without API key](https://ethereum.stackexchange.com/questions/34190/does-etherscan-require-the-use-of-an-api-key))*. +You can use library *with or without* API key *([Check API request\sec restrictions when used without API key](https://docs.etherscan.io/getting-started/viewing-api-usage-statistics))*. -Library will automatically limit requests up to **5 req/sec** when used *without* key. +Library will automatically limit requests up to **1 requests in 5 seconds** when used *without* key and up to **5 requests in 1 seconds** when used with API KEY (free plan). ```java -EtherScanApi api = new EtherScanApi(); -EtherScanApi api = new EtherScanApi("YourApiKey"); +EtherScanAPI.builder() + .withApiKey(ApiRunner.API_KEY) + .build(); ``` Below are examples for each API category. -### Account Api +### Account API **Get Ether Balance for a single Address** - ```java -EtherScanApi api = new EtherScanApi(); +EtherScanAPI api = EtherScanAPI.build(); Balance balance = api.account().balance("0x8d4426f94e42f721C7116E81d6688cd935cB3b4F"); ``` -### Block Api +### Block API **Get uncles block for block height** - ```java -EtherScanApi api = new EtherScanApi(); +EtherScanAPI api = EtherScanAPI.build(); Optional uncles = api.block().uncles(200000); ``` -### Contract Api +### Contract API **Request contract ABI from [verified codes](https://etherscan.io/contractsVerified)** ```java -EtherScanApi api = new EtherScanApi(); +EtherScanAPI api = EtherScanAPI.build(); Abi abi = api.contract().contractAbi("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413"); ``` -### Logs Api +### Logs API **Get event logs for single topic** - ```java -EtherScanApi api = new EtherScanApi(); +EtherScanAPI api = EtherScanAPI.build(); LogQuery query = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") .build(); @@ -126,58 +128,55 @@ List logs = api.logs().logs(query); ``` **Get event logs for 3 topics with respectful operations** - ```java -EtherScanApi api = new EtherScanApi(); -LogQuery query = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c", 379224, 400000) - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", - "0x72657075746174696f6e00000000000000000000000000000000000000000000", - "0x72657075746174696f6e00000000000000000000000000000000000000000000") +EtherScanAPI api = EtherScanAPI.build(); +LogQuery query = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withBlockFrom(379224) + .withBlockTo(400000) + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", + "0x72657075746174696f6e00000000000000000000000000000000000000000000", + "0x72657075746174696f6e00000000000000000000000000000000000000000000") .setOpTopic0_1(LogOp.AND) - .setOpTopic0_2(LogOp.OR) + .setOpTopic0_2(null) .setOpTopic1_2(LogOp.AND) .build(); List logs = api.logs().logs(query); ``` -### Proxy Api - -**Get tx detailds with proxy endpoint** +### Proxy API +**Get tx details with proxy endpoint** ```java -EtherScanApi api = new EtherScanApi(EthNetwork.MAINNET); +EtherScanAPI api = EtherScanAPI.build(); Optional tx = api.proxy().tx("0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); ``` **Get block info with proxy endpoint** - ```java -EtherScanApi api = new EtherScanApi(EthNetwork.MAINNET); +EtherScanAPI api = EtherScanAPI.build(); Optional block = api.proxy().block(15215); ``` -### Stats Api +### Stats API **Statistic about last price** - ```java -EtherScanApi api = new EtherScanApi(); +EtherScanAPI api = EtherScanAPI.build(); Price price = api.stats().lastPrice(); ``` -### Transaction Api +### Transaction API **Request receipt status for tx** - ```java -EtherScanApi api = new EtherScanApi(); +EtherScanAPI api = EtherScanAPI.build(); Optional status = api.txs().receiptStatus("0x513c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76"); ``` -### Token Api +### Token API -You can read about token API [here](https://etherscan.io/apis#tokens) +You can read about token API [here](https://docs.etherscan.io/api-endpoints/tokens) Token API methods migrated to [Account](#account-api) & [Stats](#stats-api) respectfully. diff --git a/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java index 1b7bce6..11bb192 100644 --- a/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java @@ -64,7 +64,7 @@ public Balance balance(String address) throws EtherScanException { if (response.getStatus() != 1) throw new EtherScanResponseException(response); - return new Balance(address, new BigInteger(response.getResult())); + return new Balance(address, new Wei(new BigInteger(response.getResult()))); } @NotNull @@ -78,7 +78,7 @@ public TokenBalance balance(String address, String contract) throws EtherScanExc if (response.getStatus() != 1) throw new EtherScanResponseException(response); - return new TokenBalance(address, new BigInteger(response.getResult()), contract); + return new TokenBalance(address, new Wei(new BigInteger(response.getResult())), contract); } @NotNull @@ -101,7 +101,7 @@ public List balances(List addresses) throws EtherScanException if (!BasicUtils.isEmpty(response.getResult())) balances.addAll(response.getResult().stream() - .map(r -> new Balance(r.getAccount(), new BigInteger(r.getBalance()))) + .map(r -> new Balance(r.getAccount(), new Wei(new BigInteger(r.getBalance())))) .collect(Collectors.toList())); } diff --git a/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java index 6da3d8f..dffb1aa 100644 --- a/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java +++ b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java @@ -37,6 +37,11 @@ public interface EtherScanAPI extends AutoCloseable { @NotNull GasTrackerAPI gasTracker(); + @NotNull + static EtherScanAPI build() { + return builder().build(); + } + @NotNull static Builder builder() { return new EthScanAPIBuilder(); diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Balance.java b/src/main/java/io/goodforgod/api/etherscan/model/Balance.java index 783b7d8..079d4b6 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Balance.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Balance.java @@ -1,6 +1,5 @@ package io.goodforgod.api.etherscan.model; -import java.math.BigInteger; import java.util.Objects; /** @@ -13,9 +12,9 @@ public class Balance { private final Wei balance; private final String address; - public Balance(String address, BigInteger balance) { + public Balance(String address, Wei balance) { this.address = address; - this.balance = new Wei(balance); + this.balance = balance; } // diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TokenBalance.java b/src/main/java/io/goodforgod/api/etherscan/model/TokenBalance.java index d42fd05..0c1a5b5 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TokenBalance.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TokenBalance.java @@ -1,6 +1,5 @@ package io.goodforgod.api.etherscan.model; -import java.math.BigInteger; import java.util.Objects; /** @@ -11,7 +10,7 @@ public class TokenBalance extends Balance { private final String tokenContract; - public TokenBalance(String address, BigInteger balance, String tokenContract) { + public TokenBalance(String address, Wei balance, String tokenContract) { super(address, balance); this.tokenContract = tokenContract; } diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceTests.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceTests.java index f22a724..ed537c6 100644 --- a/src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountBalanceTests.java @@ -18,11 +18,7 @@ class AccountBalanceTests extends ApiRunner { void correct() { Balance balance = api.account().balance("0x8d4426f94e42f721C7116E81d6688cd935cB3b4F"); assertNotNull(balance); - assertNotNull(balance.getWei()); - assertNotNull(balance.getMwei()); - assertNotNull(balance.getKwei()); - assertNotNull(balance.getGwei()); - assertNotNull(balance.getEther()); + assertNotNull(balance.getBalanceInWei()); assertNotNull(balance.getAddress()); assertNotNull(balance.toString()); } @@ -37,8 +33,8 @@ void invalidParamWithError() { void correctParamWithEmptyExpectedResult() { Balance balance = api.account().balance("0x1d4426f94e42f721C7116E81d6688cd935cB3b4F"); assertNotNull(balance); - assertNotNull(balance.getWei()); + assertNotNull(balance.getBalanceInWei()); assertNotNull(balance.getAddress()); - assertEquals(0, balance.getWei().intValue()); + assertEquals(0, balance.getBalanceInWei().asWei().intValue()); } } diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountTokenBalanceTests.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTokenBalanceTests.java index 4a7d921..3919982 100644 --- a/src/test/java/io/goodforgod/api/etherscan/account/AccountTokenBalanceTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTokenBalanceTests.java @@ -19,12 +19,12 @@ void correct() { TokenBalance balance = api.account().balance("0x5d807e7F124EC2103a59c5249187f772c0b8D6b2", "0x5EaC95ad5b287cF44E058dCf694419333b796123"); assertNotNull(balance); - assertNotNull(balance.getWei()); + assertNotNull(balance.getBalanceInWei()); assertNotNull(balance.getAddress()); assertNotNull(balance.getContract()); assertNotNull(balance.toString()); - TokenBalance balance2 = new TokenBalance("125161", balance.getWei(), balance.getContract()); + TokenBalance balance2 = new TokenBalance("125161", balance.getBalanceInWei(), balance.getContract()); assertNotEquals(balance, balance2); assertNotEquals(balance.hashCode(), balance2.hashCode()); } @@ -48,9 +48,9 @@ void correctParamWithEmptyExpectedResult() { TokenBalance balance = api.account().balance("0x1d807e7F124EC2103a59c5249187f772c0b8D6b2", "0x5EaC95ad5b287cF44E058dCf694419333b796123"); assertNotNull(balance); - assertNotNull(balance.getWei()); + assertNotNull(balance.getBalanceInWei()); assertNotNull(balance.getAddress()); assertNotNull(balance.getContract()); - assertEquals(0, balance.getWei().intValue()); + assertEquals(0, balance.getBalanceInWei().asWei().intValue()); } } From 3210c397fa652629f1ff0ba7cc7e198b26879976 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 14 May 2023 21:39:08 +0300 Subject: [PATCH 33/61] [2.0.0-SNAPSHOT] EthHttpClient contract refactored to response with byte[] Converter contract refactored to receive byte[] --- .../api/etherscan/BasicProvider.java | 16 +++---- .../api/etherscan/BlockAPIProvider.java | 8 +++- .../api/etherscan/ContractAPIProvider.java | 3 +- .../goodforgod/api/etherscan/Converter.java | 2 +- .../api/etherscan/EthScanAPIBuilder.java | 6 ++- .../api/etherscan/http/EthHttpClient.java | 6 +-- .../etherscan/http/impl/UrlEthHttpClient.java | 42 +++++++++---------- .../manager/RequestQueueManager.java | 7 +++- .../goodforgod/api/etherscan/model/Wei.java | 4 ++ .../etherscan/model/ModelBuilderTests.java | 1 + 10 files changed, 54 insertions(+), 41 deletions(-) diff --git a/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java b/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java index f1867d1..998f475 100644 --- a/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java @@ -7,7 +7,6 @@ import io.goodforgod.api.etherscan.http.EthHttpClient; import io.goodforgod.api.etherscan.manager.RequestQueueManager; import io.goodforgod.api.etherscan.model.response.StringResponseTO; -import io.goodforgod.api.etherscan.util.BasicUtils; import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.Map; @@ -44,7 +43,7 @@ abstract class BasicProvider { this.converter = converter; } - T convert(String json, Class tClass) { + T convert(byte[] json, Class tClass) { try { final T t = converter.fromJson(json, tClass); if (t instanceof StringResponseTO && ((StringResponseTO) t).getResult().startsWith("Max rate limit reached")) { @@ -53,32 +52,33 @@ T convert(String json, Class tClass) { return t; } catch (Exception e) { + final String jsonAsString = new String(json, StandardCharsets.UTF_8); try { final Map map = converter.fromJson(json, Map.class); final Object result = map.get("result"); if (result instanceof String && ((String) result).startsWith("Max rate limit reached")) throw new EtherScanRateLimitException(((String) result)); - throw new EtherScanParseException(e.getMessage() + ", for response: " + json, e.getCause(), json); + throw new EtherScanParseException(e.getMessage() + ", for response: " + jsonAsString, e.getCause(), jsonAsString); } catch (EtherScanException ex) { throw ex; } catch (Exception ex) { - throw new EtherScanParseException(e.getMessage() + ", for response: " + json, e.getCause(), json); + throw new EtherScanParseException(e.getMessage() + ", for response: " + jsonAsString, e.getCause(), jsonAsString); } } } - String getRequest(String urlParameters) { + byte[] getRequest(String urlParameters) { queue.takeTurn(); final URI uri = URI.create(baseUrl + module + urlParameters); - final String result = executor.get(uri); - if (BasicUtils.isEmpty(result)) + final byte[] result = executor.get(uri); + if (result.length == 0) throw new EtherScanResponseException("Server returned null value for GET request at URL - " + uri); return result; } - String postRequest(String urlParameters, String dataToPost) { + 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)); diff --git a/src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java index 98a2d90..41d86dd 100644 --- a/src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java @@ -33,11 +33,15 @@ final class BlockAPIProvider extends BasicProvider implements BlockAPI { @Override public Optional uncles(long blockNumber) throws EtherScanException { final String urlParam = ACT_BLOCK_PARAM + BLOCKNO_PARAM + blockNumber; - final String response = getRequest(urlParam); - if (BasicUtils.isEmpty(response) || response.contains("NOTOK")) + final byte[] response = getRequest(urlParam); + if (response.length == 0) return Optional.empty(); final UncleBlockResponseTO responseTO = convert(response, UncleBlockResponseTO.class); + if (responseTO.getMessage().equals("NOTOK")) { + return Optional.empty(); + } + BasicUtils.validateTxResponse(responseTO); return (responseTO.getResult() == null || responseTO.getResult().isEmpty()) ? Optional.empty() diff --git a/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java index 1a8fa9a..7b75240 100644 --- a/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java @@ -36,8 +36,9 @@ public Abi contractAbi(String address) throws EtherScanException { final String urlParam = ACT_ABI_PARAM + ADDRESS_PARAM + address; final StringResponseTO response = getRequest(urlParam, StringResponseTO.class); - if (response.getStatus() != 1 && "NOTOK".equals(response.getMessage())) + if (response.getStatus() != 1 && "NOTOK".equals(response.getMessage())) { throw new EtherScanResponseException(response); + } return (response.getResult().startsWith("Contract sou")) ? Abi.nonVerified() diff --git a/src/main/java/io/goodforgod/api/etherscan/Converter.java b/src/main/java/io/goodforgod/api/etherscan/Converter.java index e8c577a..4025839 100644 --- a/src/main/java/io/goodforgod/api/etherscan/Converter.java +++ b/src/main/java/io/goodforgod/api/etherscan/Converter.java @@ -9,5 +9,5 @@ public interface Converter { @NotNull - T fromJson(@NotNull String json, @NotNull Class type); + T fromJson(byte[] jsonAsByteArray, @NotNull Class type); } diff --git a/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java b/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java index c9c1102..69474d9 100644 --- a/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java +++ b/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java @@ -7,6 +7,7 @@ import io.goodforgod.api.etherscan.manager.RequestQueueManager; import io.goodforgod.api.etherscan.util.BasicUtils; import io.goodforgod.gson.configuration.GsonConfiguration; +import java.nio.charset.StandardCharsets; import java.util.function.Supplier; import org.jetbrains.annotations.NotNull; @@ -28,8 +29,9 @@ final class EthScanAPIBuilder implements EtherScanAPI.Builder { private Supplier converterSupplier = () -> new Converter() { @Override - public @NotNull T fromJson(@NotNull String json, @NotNull Class type) { - return gson.fromJson(json, type); + public @NotNull T fromJson(byte[] jsonAsByteArray, @NotNull Class type) { + final String jsonAsString = new String(jsonAsByteArray, StandardCharsets.UTF_8); + return gson.fromJson(jsonAsString, type); } }; diff --git a/src/main/java/io/goodforgod/api/etherscan/http/EthHttpClient.java b/src/main/java/io/goodforgod/api/etherscan/http/EthHttpClient.java index f4b559d..bd01f83 100644 --- a/src/main/java/io/goodforgod/api/etherscan/http/EthHttpClient.java +++ b/src/main/java/io/goodforgod/api/etherscan/http/EthHttpClient.java @@ -17,8 +17,7 @@ public interface EthHttpClient { * @param uri as string * @return result as string */ - @NotNull - String get(@NotNull URI uri); + byte[] get(@NotNull URI uri); /** * Performs a Http POST request @@ -27,6 +26,5 @@ public interface EthHttpClient { * @param body to post * @return result as string */ - @NotNull - String post(@NotNull URI uri, byte[] body); + byte[] post(@NotNull URI uri, byte[] body); } diff --git a/src/main/java/io/goodforgod/api/etherscan/http/impl/UrlEthHttpClient.java b/src/main/java/io/goodforgod/api/etherscan/http/impl/UrlEthHttpClient.java index 57c970b..b298743 100644 --- a/src/main/java/io/goodforgod/api/etherscan/http/impl/UrlEthHttpClient.java +++ b/src/main/java/io/goodforgod/api/etherscan/http/impl/UrlEthHttpClient.java @@ -5,15 +5,11 @@ import io.goodforgod.api.etherscan.error.EtherScanConnectionException; import io.goodforgod.api.etherscan.error.EtherScanTimeoutException; import io.goodforgod.api.etherscan.http.EthHttpClient; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStream; +import java.io.*; import java.net.HttpURLConnection; import java.net.SocketTimeoutException; import java.net.URI; import java.net.URL; -import java.nio.charset.StandardCharsets; import java.time.Duration; import java.util.Collections; import java.util.HashMap; @@ -83,7 +79,7 @@ private HttpURLConnection buildConnection(URI uri, String method) throws IOExcep } @Override - public @NotNull String get(@NotNull URI uri) { + public byte[] get(@NotNull URI uri) { try { final HttpURLConnection connection = buildConnection(uri, "GET"); final int status = connection.getResponseCode(); @@ -95,7 +91,7 @@ private HttpURLConnection buildConnection(URI uri, String method) throws IOExcep throw new EtherScanConnectionException("Server error: " + connection.getResponseMessage()); } - final String data = readData(connection); + final byte[] data = readData(connection); connection.disconnect(); return data; } catch (SocketTimeoutException e) { @@ -106,7 +102,7 @@ private HttpURLConnection buildConnection(URI uri, String method) throws IOExcep } @Override - public @NotNull String post(@NotNull URI uri, byte[] body) { + public byte[] post(@NotNull URI uri, byte[] body) { try { final HttpURLConnection connection = buildConnection(uri, "POST"); final int contentLength = body.length; @@ -129,7 +125,7 @@ private HttpURLConnection buildConnection(URI uri, String method) throws IOExcep throw new EtherScanConnectionException("Server error: " + connection.getResponseMessage()); } - final String data = readData(connection); + final byte[] data = readData(connection); connection.disconnect(); return data; } catch (SocketTimeoutException e) { @@ -139,25 +135,29 @@ private HttpURLConnection buildConnection(URI uri, String method) throws IOExcep } } - private String readData(HttpURLConnection connection) throws IOException { - final StringBuilder content = new StringBuilder(); - try (BufferedReader in = new BufferedReader(getStreamReader(connection))) { - String inputLine; - while ((inputLine = in.readLine()) != null) - content.append(inputLine); - } + private byte[] readData(HttpURLConnection connection) throws IOException { + try (ByteArrayOutputStream buffer = new ByteArrayOutputStream()) { + try (InputStream in = getStreamReader(connection)) { + byte[] data = new byte[256]; + int nRead; + while ((nRead = in.read(data, 0, data.length)) != -1) { + buffer.write(data, 0, nRead); + } + } - return content.toString(); + buffer.flush(); + return buffer.toByteArray(); + } } - private InputStreamReader getStreamReader(HttpURLConnection connection) throws IOException { + private InputStream getStreamReader(HttpURLConnection connection) throws IOException { switch (String.valueOf(connection.getContentEncoding())) { case "gzip": - return new InputStreamReader(new GZIPInputStream(connection.getInputStream()), StandardCharsets.UTF_8); + return new GZIPInputStream(connection.getInputStream()); case "deflate": - return new InputStreamReader(new InflaterInputStream(connection.getInputStream()), StandardCharsets.UTF_8); + return new InflaterInputStream(connection.getInputStream()); default: - return new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8); + return connection.getInputStream(); } } } diff --git a/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java b/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java index 46a76e2..2fdfe82 100644 --- a/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java +++ b/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java @@ -5,8 +5,8 @@ import java.time.Duration; /** - * 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 + * Manager grants turn if the limit is not exhausted And resets queue each set period * * @author GoodforGod * @since 30.10.2018 @@ -23,6 +23,9 @@ public interface RequestQueueManager extends AutoCloseable { * Free API KEY */ RequestQueueManager FREE_PLAN = new SemaphoreRequestQueueManager(5, Duration.ofMillis(1010L)); + RequestQueueManager STANDARD_PLAN = new SemaphoreRequestQueueManager(10, Duration.ofMillis(1010L)); + RequestQueueManager ADVANCED_PLAN = new SemaphoreRequestQueueManager(20, Duration.ofMillis(1010L)); + RequestQueueManager PROFESSIONAL_PLAN = new SemaphoreRequestQueueManager(30, Duration.ofMillis(1010L)); RequestQueueManager UNLIMITED = new FakeRequestQueueManager(); diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Wei.java b/src/main/java/io/goodforgod/api/etherscan/model/Wei.java index cb136df..e23ea51 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Wei.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Wei.java @@ -11,6 +11,10 @@ public class Wei { private final BigInteger result; + public Wei(int value) { + this.result = BigInteger.valueOf(value); + } + public Wei(long value) { this.result = BigInteger.valueOf(value); } diff --git a/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java b/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java index 7db6aae..1bec491 100644 --- a/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java @@ -252,6 +252,7 @@ void txInternalBuilder() { .withContractAddress("1") .withFrom("1") .withTo("1") + .withValue(BigInteger.ONE) .withGas(BigInteger.ONE) .withGasUsed(BigInteger.ONE) .withHash("1") From f5b2edb7db033600bc7e54b4c60836306554c5a6 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 14 May 2023 22:06:44 +0300 Subject: [PATCH 34/61] [2.0.0-SNAPSHOT] EtherScanLogQueryException name fixed EtherScanResponseException response entity added StringResponseTO#builder added BasicProvider error handling improved --- .../api/etherscan/BasicProvider.java | 26 +++++++++++--- .../api/etherscan/BlockAPIProvider.java | 30 ++++++++++------ .../api/etherscan/ContractAPIProvider.java | 2 +- .../api/etherscan/EtherScanAPI.java | 5 --- .../api/etherscan/ProxyAPIProvider.java | 12 +++++-- ...n.java => EtherScanLogQueryException.java} | 4 +-- .../error/EtherScanResponseException.java | 14 ++++++-- .../model/query/LogQueryBuilderImpl.java | 24 ++++++------- .../etherscan/model/query/LogTopicQuadro.java | 14 ++++---- .../etherscan/model/query/LogTopicSingle.java | 4 +-- .../etherscan/model/query/LogTopicTriple.java | 10 +++--- .../etherscan/model/query/LogTopicTuple.java | 6 ++-- .../model/response/BaseResponseTO.java | 4 +-- .../model/response/StringResponseTO.java | 36 +++++++++++++++++++ .../api/etherscan/util/BasicUtils.java | 12 +++++-- .../etherscan/logs/LogQueryBuilderTests.java | 36 +++++++++---------- 16 files changed, 160 insertions(+), 79 deletions(-) rename src/main/java/io/goodforgod/api/etherscan/error/{ErtherScanLogQueryException.java => EtherScanLogQueryException.java} (50%) diff --git a/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java b/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java index 998f475..3c88f3b 100644 --- a/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java @@ -20,6 +20,8 @@ */ abstract class BasicProvider { + private static final String MAX_RATE_LIMIT_REACHED = "Max rate limit reached"; + static final int MAX_END_BLOCK = Integer.MAX_VALUE; static final int MIN_START_BLOCK = 0; @@ -46,17 +48,26 @@ abstract class BasicProvider { T convert(byte[] json, Class tClass) { try { final T t = converter.fromJson(json, tClass); - if (t instanceof StringResponseTO && ((StringResponseTO) t).getResult().startsWith("Max rate limit reached")) { + if (t instanceof StringResponseTO && ((StringResponseTO) t).getResult().startsWith(MAX_RATE_LIMIT_REACHED)) { throw new EtherScanRateLimitException(((StringResponseTO) t).getResult()); } return t; } catch (Exception e) { + final StringResponseTO response = converter.fromJson(json, StringResponseTO.class); + if (response.getResult() != null && response.getStatus() == 0) { + if (response.getResult().startsWith(MAX_RATE_LIMIT_REACHED)) { + throw new EtherScanRateLimitException(response.getResult()); + } else { + throw new EtherScanResponseException(response); + } + } + final String jsonAsString = new String(json, StandardCharsets.UTF_8); try { final Map map = converter.fromJson(json, Map.class); final Object result = map.get("result"); - if (result instanceof String && ((String) result).startsWith("Max rate limit reached")) + if (result instanceof String && ((String) result).startsWith(MAX_RATE_LIMIT_REACHED)) throw new EtherScanRateLimitException(((String) result)); throw new EtherScanParseException(e.getMessage() + ", for response: " + jsonAsString, e.getCause(), jsonAsString); @@ -72,8 +83,15 @@ byte[] getRequest(String urlParameters) { queue.takeTurn(); final URI uri = URI.create(baseUrl + module + urlParameters); final byte[] result = executor.get(uri); - if (result.length == 0) - throw new EtherScanResponseException("Server returned null value for GET request at URL - " + uri); + if (result.length == 0) { + final StringResponseTO emptyResponse = StringResponseTO.builder() + .withStatus("0") + .withMessage("Server returned null value for GET request at URL - " + uri) + .withResult("") + .build(); + + throw new EtherScanResponseException(emptyResponse, "Server returned null value for GET request at URL - " + uri); + } return result; } diff --git a/src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java index 41d86dd..406ac19 100644 --- a/src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java @@ -1,6 +1,7 @@ package io.goodforgod.api.etherscan; import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.error.EtherScanResponseException; import io.goodforgod.api.etherscan.http.EthHttpClient; import io.goodforgod.api.etherscan.manager.RequestQueueManager; import io.goodforgod.api.etherscan.model.BlockUncle; @@ -12,8 +13,8 @@ /** * Block API Implementation * - * @see BlockAPI * @author GoodforGod + * @see BlockAPI * @since 28.10.2018 */ final class BlockAPIProvider extends BasicProvider implements BlockAPI { @@ -34,17 +35,26 @@ final class BlockAPIProvider extends BasicProvider implements BlockAPI { public Optional uncles(long blockNumber) throws EtherScanException { final String urlParam = ACT_BLOCK_PARAM + BLOCKNO_PARAM + blockNumber; final byte[] response = getRequest(urlParam); - if (response.length == 0) - return Optional.empty(); - - final UncleBlockResponseTO responseTO = convert(response, UncleBlockResponseTO.class); - if (responseTO.getMessage().equals("NOTOK")) { + if (response.length == 0) { return Optional.empty(); } - BasicUtils.validateTxResponse(responseTO); - return (responseTO.getResult() == null || responseTO.getResult().isEmpty()) - ? Optional.empty() - : Optional.of(responseTO.getResult()); + try { + final UncleBlockResponseTO responseTO = convert(response, UncleBlockResponseTO.class); + if (responseTO.getMessage().startsWith("NOTOK")) { + return Optional.empty(); + } + + BasicUtils.validateTxResponse(responseTO); + return (responseTO.getResult() == null || responseTO.getResult().isEmpty()) + ? Optional.empty() + : Optional.of(responseTO.getResult()); + } catch (EtherScanResponseException e) { + if (e.getResponse().getMessage().startsWith("NOTOK")) { + return Optional.empty(); + } else { + throw e; + } + } } } diff --git a/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java index 7b75240..bbb7335 100644 --- a/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java @@ -36,7 +36,7 @@ public Abi contractAbi(String address) throws EtherScanException { final String urlParam = ACT_ABI_PARAM + ADDRESS_PARAM + address; final StringResponseTO response = getRequest(urlParam, StringResponseTO.class); - if (response.getStatus() != 1 && "NOTOK".equals(response.getMessage())) { + if (response.getStatus() != 1 && response.getMessage().startsWith("NOTOK")) { throw new EtherScanResponseException(response); } diff --git a/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java index dffb1aa..6da3d8f 100644 --- a/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java +++ b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java @@ -37,11 +37,6 @@ public interface EtherScanAPI extends AutoCloseable { @NotNull GasTrackerAPI gasTracker(); - @NotNull - static EtherScanAPI build() { - return builder().build(); - } - @NotNull static Builder builder() { return new EthScanAPIBuilder(); diff --git a/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java index 27e00df..a33f7c1 100644 --- a/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java @@ -13,6 +13,7 @@ import io.goodforgod.api.etherscan.model.proxy.utility.StringProxyTO; import io.goodforgod.api.etherscan.model.proxy.utility.TxInfoProxyTO; import io.goodforgod.api.etherscan.model.proxy.utility.TxProxyTO; +import io.goodforgod.api.etherscan.model.response.StringResponseTO; import io.goodforgod.api.etherscan.util.BasicUtils; import java.util.Optional; import java.util.regex.Pattern; @@ -142,10 +143,17 @@ public Optional txSendRaw(String hexEncodedTx) throws EtherScanException final String urlParams = ACT_SEND_RAW_TX_PARAM + HEX_PARAM + hexEncodedTx; final StringProxyTO response = postRequest(urlParams, "", StringProxyTO.class); - if (response.getError() != null) - throw new EtherScanResponseException("Error occurred with code " + response.getError().getCode() + if (response.getError() != null) { + final StringResponseTO responseError = StringResponseTO.builder() + .withStatus("0") + .withMessage(response.getError().getMessage()) + .withResult(response.getError().getCode()) + .build(); + + throw new EtherScanResponseException(responseError, "Error occurred with code " + response.getError().getCode() + " with message " + response.getError().getMessage() + ", error id " + response.getId() + ", jsonRPC " + response.getJsonrpc()); + } return Optional.ofNullable(response.getResult()); } diff --git a/src/main/java/io/goodforgod/api/etherscan/error/ErtherScanLogQueryException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanLogQueryException.java similarity index 50% rename from src/main/java/io/goodforgod/api/etherscan/error/ErtherScanLogQueryException.java rename to src/main/java/io/goodforgod/api/etherscan/error/EtherScanLogQueryException.java index b39dcee..e72d682 100644 --- a/src/main/java/io/goodforgod/api/etherscan/error/ErtherScanLogQueryException.java +++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanLogQueryException.java @@ -4,9 +4,9 @@ * @author GoodforGod * @since 31.10.2018 */ -public class ErtherScanLogQueryException extends EtherScanException { +public class EtherScanLogQueryException extends EtherScanException { - public ErtherScanLogQueryException(String message) { + public EtherScanLogQueryException(String message) { super(message); } } diff --git a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanResponseException.java b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanResponseException.java index 21da798..19785ce 100644 --- a/src/main/java/io/goodforgod/api/etherscan/error/EtherScanResponseException.java +++ b/src/main/java/io/goodforgod/api/etherscan/error/EtherScanResponseException.java @@ -9,15 +9,23 @@ */ public class EtherScanResponseException extends EtherScanException { + private final transient BaseResponseTO response; + public EtherScanResponseException(BaseResponseTO response) { - this(response.getMessage() + ", with status: " + response.getStatus()); + this(response, response.getMessage() + ", with status: " + response.getStatus()); } public EtherScanResponseException(StringResponseTO response) { - this(response.getResult() + ", with status: " + response.getStatus() + ", with message: " + response.getMessage()); + this(response, + response.getResult() + ", with status: " + response.getStatus() + ", with message: " + response.getMessage()); } - public EtherScanResponseException(String message) { + public EtherScanResponseException(BaseResponseTO response, String message) { super(message); + this.response = response; + } + + public BaseResponseTO getResponse() { + return response; } } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryBuilderImpl.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryBuilderImpl.java index 716cfa4..549bd47 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryBuilderImpl.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogQueryBuilderImpl.java @@ -1,7 +1,7 @@ package io.goodforgod.api.etherscan.model.query; import io.goodforgod.api.etherscan.LogsAPI; -import io.goodforgod.api.etherscan.error.ErtherScanLogQueryException; +import io.goodforgod.api.etherscan.error.EtherScanLogQueryException; import io.goodforgod.api.etherscan.util.BasicUtils; import org.jetbrains.annotations.NotNull; @@ -40,27 +40,27 @@ final class LogQueryBuilderImpl implements LogQuery.Builder { @Override public @NotNull LogTopicSingle withTopic(@NotNull String topic0) { if (BasicUtils.isNotHex(topic0)) - throw new ErtherScanLogQueryException("topic0 can not be empty or non hex."); + throw new EtherScanLogQueryException("topic0 can not be empty or non hex."); return new LogTopicSingle(address, startBlock, endBlock, topic0); } @Override public @NotNull LogTopicTuple withTopic(@NotNull String topic0, @NotNull String topic1) { if (BasicUtils.isNotHex(topic0)) - throw new ErtherScanLogQueryException("topic0 can not be empty or non hex."); + throw new EtherScanLogQueryException("topic0 can not be empty or non hex."); if (BasicUtils.isNotHex(topic1)) - throw new ErtherScanLogQueryException("topic1 can not be empty or non hex."); + throw new EtherScanLogQueryException("topic1 can not be empty or non hex."); return new LogTopicTuple(address, startBlock, endBlock, topic0, topic1); } @Override public @NotNull LogTopicTriple withTopic(@NotNull String topic0, @NotNull String topic1, @NotNull String topic2) { if (BasicUtils.isNotHex(topic0)) - throw new ErtherScanLogQueryException("topic0 can not be empty or non hex."); + throw new EtherScanLogQueryException("topic0 can not be empty or non hex."); if (BasicUtils.isNotHex(topic1)) - throw new ErtherScanLogQueryException("topic1 can not be empty or non hex."); + throw new EtherScanLogQueryException("topic1 can not be empty or non hex."); if (BasicUtils.isNotHex(topic2)) - throw new ErtherScanLogQueryException("topic2 can not be empty or non hex."); + throw new EtherScanLogQueryException("topic2 can not be empty or non hex."); return new LogTopicTriple(address, startBlock, endBlock, topic0, topic1, topic2); } @@ -68,19 +68,19 @@ final class LogQueryBuilderImpl implements LogQuery.Builder { public @NotNull LogTopicQuadro withTopic(@NotNull String topic0, @NotNull String topic1, @NotNull String topic2, @NotNull String topic3) { if (BasicUtils.isNotHex(topic0)) - throw new ErtherScanLogQueryException("topic0 can not be empty or non hex."); + throw new EtherScanLogQueryException("topic0 can not be empty or non hex."); if (BasicUtils.isNotHex(topic1)) - throw new ErtherScanLogQueryException("topic1 can not be empty or non hex."); + throw new EtherScanLogQueryException("topic1 can not be empty or non hex."); if (BasicUtils.isNotHex(topic2)) - throw new ErtherScanLogQueryException("topic2 can not be empty or non hex."); + throw new EtherScanLogQueryException("topic2 can not be empty or non hex."); if (BasicUtils.isNotHex(topic3)) - throw new ErtherScanLogQueryException("topic3 can not be empty or non hex."); + throw new EtherScanLogQueryException("topic3 can not be empty or non hex."); return new LogTopicQuadro(address, startBlock, endBlock, topic0, topic1, topic2, topic3); } @Override - public @NotNull LogQuery build() throws ErtherScanLogQueryException { + public @NotNull LogQuery build() throws EtherScanLogQueryException { return new LogQueryImpl("&address=" + this.address + "&fromBlock=" + this.startBlock + "&toBlock=" + this.endBlock); } } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicQuadro.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicQuadro.java index 1469f97..7fdd9db 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicQuadro.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicQuadro.java @@ -3,7 +3,7 @@ import static io.goodforgod.api.etherscan.model.query.LogQueryParams.*; import io.goodforgod.api.etherscan.LogsAPI; -import io.goodforgod.api.etherscan.error.ErtherScanLogQueryException; +import io.goodforgod.api.etherscan.error.EtherScanLogQueryException; import org.jetbrains.annotations.NotNull; /** @@ -71,17 +71,17 @@ public LogTopicQuadro setOpTopic1_3(LogOp topic1_3_opr) { @Override public @NotNull LogQuery build() { if (topic0_1_opr == null) - throw new ErtherScanLogQueryException("topic0_1_opr can not be null."); + throw new EtherScanLogQueryException("topic0_1_opr can not be null."); if (topic0_2_opr == null) - throw new ErtherScanLogQueryException("topic0_2_opr can not be null."); + throw new EtherScanLogQueryException("topic0_2_opr can not be null."); if (topic0_3_opr == null) - throw new ErtherScanLogQueryException("topic0_3_opr can not be null."); + throw new EtherScanLogQueryException("topic0_3_opr can not be null."); if (topic1_2_opr == null) - throw new ErtherScanLogQueryException("topic1_2_opr can not be null."); + throw new EtherScanLogQueryException("topic1_2_opr can not be null."); if (topic2_3_opr == null) - throw new ErtherScanLogQueryException("topic2_3_opr can not be null."); + throw new EtherScanLogQueryException("topic2_3_opr can not be null."); if (topic1_3_opr == null) - throw new ErtherScanLogQueryException("topic1_3_opr can not be null."); + throw new EtherScanLogQueryException("topic1_3_opr can not be null."); return new LogQueryImpl(ADDRESS_PARAM + address + FROM_BLOCK_PARAM + startBlock + TO_BLOCK_PARAM + endBlock diff --git a/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicSingle.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicSingle.java index 85bd18c..a736ffa 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicSingle.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicSingle.java @@ -3,7 +3,7 @@ import static io.goodforgod.api.etherscan.model.query.LogQueryParams.*; import io.goodforgod.api.etherscan.LogsAPI; -import io.goodforgod.api.etherscan.error.ErtherScanLogQueryException; +import io.goodforgod.api.etherscan.error.EtherScanLogQueryException; import org.jetbrains.annotations.NotNull; /** @@ -29,7 +29,7 @@ public final class LogTopicSingle implements LogTopicBuilder { } @Override - public @NotNull LogQuery build() throws ErtherScanLogQueryException { + public @NotNull LogQuery build() throws EtherScanLogQueryException { return new LogQueryImpl(ADDRESS_PARAM + address + FROM_BLOCK_PARAM + startBlock + TO_BLOCK_PARAM + endBlock + TOPIC_0_PARAM + topic0); diff --git a/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTriple.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTriple.java index d56edb5..ac9efb8 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTriple.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTriple.java @@ -3,7 +3,7 @@ import static io.goodforgod.api.etherscan.model.query.LogQueryParams.*; import io.goodforgod.api.etherscan.LogsAPI; -import io.goodforgod.api.etherscan.error.ErtherScanLogQueryException; +import io.goodforgod.api.etherscan.error.EtherScanLogQueryException; import org.jetbrains.annotations.NotNull; /** @@ -52,13 +52,13 @@ public LogTopicTriple setOpTopic1_2(LogOp topic1_2_opr) { } @Override - public @NotNull LogQuery build() throws ErtherScanLogQueryException { + public @NotNull LogQuery build() throws EtherScanLogQueryException { if (topic0_1_opr == null) - throw new ErtherScanLogQueryException("topic0_1_opr can not be null."); + throw new EtherScanLogQueryException("topic0_1_opr can not be null."); if (topic0_2_opr == null) - throw new ErtherScanLogQueryException("topic0_2_opr can not be null."); + throw new EtherScanLogQueryException("topic0_2_opr can not be null."); if (topic1_2_opr == null) - throw new ErtherScanLogQueryException("topic1_2_opr can not be null."); + throw new EtherScanLogQueryException("topic1_2_opr can not be null."); return new LogQueryImpl(ADDRESS_PARAM + address + FROM_BLOCK_PARAM + startBlock + TO_BLOCK_PARAM + endBlock diff --git a/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTuple.java b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTuple.java index 95a78a4..2ef2bba 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTuple.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/query/LogTopicTuple.java @@ -3,7 +3,7 @@ import static io.goodforgod.api.etherscan.model.query.LogQueryParams.*; import io.goodforgod.api.etherscan.LogsAPI; -import io.goodforgod.api.etherscan.error.ErtherScanLogQueryException; +import io.goodforgod.api.etherscan.error.EtherScanLogQueryException; import org.jetbrains.annotations.NotNull; /** @@ -40,9 +40,9 @@ public LogTopicTuple setOpTopic0_1(LogOp topic0_1_opr) { } @Override - public @NotNull LogQuery build() throws ErtherScanLogQueryException { + public @NotNull LogQuery build() throws EtherScanLogQueryException { if (topic0_1_opr == null) - throw new ErtherScanLogQueryException("topic0_1_opr can not be null."); + throw new EtherScanLogQueryException("topic0_1_opr can not be null."); return new LogQueryImpl(ADDRESS_PARAM + address + FROM_BLOCK_PARAM + startBlock + TO_BLOCK_PARAM + endBlock diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/BaseResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/BaseResponseTO.java index 3e100d1..46c6ca0 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/response/BaseResponseTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/BaseResponseTO.java @@ -8,8 +8,8 @@ */ public abstract class BaseResponseTO { - private String status; - private String message; + String status; + String message; public int getStatus() { return BasicUtils.isEmpty(status) diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/StringResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/StringResponseTO.java index 4fb9f04..19fa0a1 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/response/StringResponseTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/StringResponseTO.java @@ -11,4 +11,40 @@ public class StringResponseTO extends BaseResponseTO { public String getResult() { return result; } + + public static StringResponseBuilder builder() { + return new StringResponseBuilder(); + } + + public static final class StringResponseBuilder { + + private String status; + private String message; + private String result; + + private StringResponseBuilder() {} + + public StringResponseBuilder withStatus(String status) { + this.status = status; + return this; + } + + public StringResponseBuilder withMessage(String message) { + this.message = message; + return this; + } + + public StringResponseBuilder withResult(String result) { + this.result = result; + return this; + } + + public StringResponseTO build() { + StringResponseTO stringResponseTO = new StringResponseTO(); + stringResponseTO.status = this.status; + stringResponseTO.message = this.message; + stringResponseTO.result = this.result; + return stringResponseTO; + } + } } diff --git a/src/main/java/io/goodforgod/api/etherscan/util/BasicUtils.java b/src/main/java/io/goodforgod/api/etherscan/util/BasicUtils.java index eda3ce2..216ab62 100644 --- a/src/main/java/io/goodforgod/api/etherscan/util/BasicUtils.java +++ b/src/main/java/io/goodforgod/api/etherscan/util/BasicUtils.java @@ -5,6 +5,7 @@ import io.goodforgod.api.etherscan.error.EtherScanResponseException; import io.goodforgod.api.etherscan.model.response.BaseResponseTO; import io.goodforgod.api.etherscan.model.response.BlockParam; +import io.goodforgod.api.etherscan.model.response.StringResponseTO; import java.math.BigInteger; import java.util.*; import java.util.regex.Pattern; @@ -98,12 +99,17 @@ public static void validateTxHash(String txhash) { } public static void validateTxResponse(T response) { - if (response == null) - throw new EtherScanResponseException("EtherScan responded with null value"); + if (response == null) { + final StringResponseTO emptyResponse = StringResponseTO.builder() + .withStatus("0") + .withMessage("EtherScan responded with null value") + .build(); + throw new EtherScanResponseException(emptyResponse, "EtherScan responded with null value"); + } if (response.getStatus() != 1) { if (response.getMessage() == null) { - throw new EtherScanResponseException( + throw new EtherScanResponseException(response, "Unexpected Etherscan exception, no information from server about error, code " + response.getStatus()); } else if (!response.getMessage().startsWith("No tra") && !response.getMessage().startsWith("No rec")) { throw new EtherScanResponseException(response); diff --git a/src/test/java/io/goodforgod/api/etherscan/logs/LogQueryBuilderTests.java b/src/test/java/io/goodforgod/api/etherscan/logs/LogQueryBuilderTests.java index 339f07e..955443c 100644 --- a/src/test/java/io/goodforgod/api/etherscan/logs/LogQueryBuilderTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/logs/LogQueryBuilderTests.java @@ -1,8 +1,8 @@ package io.goodforgod.api.etherscan.logs; import io.goodforgod.api.etherscan.ApiRunner; -import io.goodforgod.api.etherscan.error.ErtherScanLogQueryException; import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.error.EtherScanLogQueryException; import io.goodforgod.api.etherscan.model.query.*; import org.junit.jupiter.api.Test; @@ -32,7 +32,7 @@ void singleInCorrectAddress() { @Test void singleInCorrectTopic() { - assertThrows(ErtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + assertThrows(EtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") .withTopic("6516=") .build()); } @@ -51,7 +51,7 @@ void tupleCorrect() { @Test void tupleInCorrectOp() { - assertThrows(ErtherScanLogQueryException.class, + assertThrows(EtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c").withBlockFrom(379224) .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000") @@ -76,7 +76,7 @@ void tripleCorrect() { @Test void tripleInCorrectOp() { - assertThrows(ErtherScanLogQueryException.class, + assertThrows(EtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c").withBlockFrom(379224).withBlockTo(400000) .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", @@ -89,7 +89,7 @@ void tripleInCorrectOp() { @Test void tripleInCorrectTopic1() { - assertThrows(ErtherScanLogQueryException.class, + assertThrows(EtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c").withBlockFrom(379224).withBlockTo(400000) .withTopic(null, "0x72657075746174696f6e00000000000000000000000000000000000000000000", @@ -102,7 +102,7 @@ void tripleInCorrectTopic1() { @Test void tripleInCorrectTopic2() { - assertThrows(ErtherScanLogQueryException.class, + assertThrows(EtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c").withBlockFrom(379224).withBlockTo(400000) .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", null, @@ -115,7 +115,7 @@ void tripleInCorrectTopic2() { @Test void tripleInCorrectTopic3() { - assertThrows(ErtherScanLogQueryException.class, + assertThrows(EtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c").withBlockFrom(379224).withBlockTo(400000) .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", @@ -147,7 +147,7 @@ void quadroCorrect() { @Test void quadroIncorrectTopic2() { - assertThrows(ErtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + assertThrows(EtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", null, "0x72657075746174696f6e00000000000000000000000000000000000000000000", @@ -163,7 +163,7 @@ void quadroIncorrectTopic2() { @Test void tupleIncorrectTopic2() { - assertThrows(ErtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + assertThrows(EtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", null) .setOpTopic0_1(LogOp.AND) @@ -172,7 +172,7 @@ void tupleIncorrectTopic2() { @Test void tupleIncorrectTopic1() { - assertThrows(ErtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + assertThrows(EtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") .withTopic(null, "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") .setOpTopic0_1(LogOp.AND) @@ -187,7 +187,7 @@ void quadroIncorrectOp1() { "0x72657075746174696f6e00000000000000000000000000000000000000000000", "0x72657075746174696f6e00000000000000000000000000000000000000000000"); - assertThrows(ErtherScanLogQueryException.class, () -> topicQuadro + assertThrows(EtherScanLogQueryException.class, () -> topicQuadro .setOpTopic0_1(null) .setOpTopic0_2(LogOp.OR) .setOpTopic0_3(LogOp.AND) @@ -205,7 +205,7 @@ void quadroIncorrectOp2() { "0x72657075746174696f6e00000000000000000000000000000000000000000000", "0x72657075746174696f6e00000000000000000000000000000000000000000000"); - assertThrows(ErtherScanLogQueryException.class, () -> topicQuadro.setOpTopic0_1(LogOp.AND) + assertThrows(EtherScanLogQueryException.class, () -> topicQuadro.setOpTopic0_1(LogOp.AND) .setOpTopic0_2(null) .setOpTopic0_3(LogOp.AND) .setOpTopic1_2(LogOp.OR) @@ -222,7 +222,7 @@ void quadroIncorrectOp3() { "0x72657075746174696f6e00000000000000000000000000000000000000000000", "0x72657075746174696f6e00000000000000000000000000000000000000000000"); - assertThrows(ErtherScanLogQueryException.class, () -> topicQuadro + assertThrows(EtherScanLogQueryException.class, () -> topicQuadro .setOpTopic0_1(LogOp.AND) .setOpTopic0_2(LogOp.OR) .setOpTopic0_3(null) @@ -234,7 +234,7 @@ void quadroIncorrectOp3() { @Test void quadroInCorrectAgainTopic() { - assertThrows(ErtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + assertThrows(EtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "0x72657075746174696f6e00000000000000000000000000000000000000000000", @@ -256,7 +256,7 @@ void quadroInCorrectOp4() { "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545"); - assertThrows(ErtherScanLogQueryException.class, () -> topicQuadro + assertThrows(EtherScanLogQueryException.class, () -> topicQuadro .setOpTopic0_1(LogOp.AND) .setOpTopic0_2(LogOp.OR) .setOpTopic0_3(LogOp.AND) @@ -274,7 +274,7 @@ void quadroInCorrectOp5() { "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545"); - assertThrows(ErtherScanLogQueryException.class, () -> topicQuadro + assertThrows(EtherScanLogQueryException.class, () -> topicQuadro .setOpTopic0_1(LogOp.AND) .setOpTopic0_2(LogOp.OR) .setOpTopic0_3(LogOp.AND) @@ -292,7 +292,7 @@ void quadroInCorrectOp6() { "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545"); - assertThrows(ErtherScanLogQueryException.class, () -> topicQuadro + assertThrows(EtherScanLogQueryException.class, () -> topicQuadro .setOpTopic0_1(LogOp.AND) .setOpTopic0_2(LogOp.OR) .setOpTopic0_3(LogOp.AND) @@ -304,7 +304,7 @@ void quadroInCorrectOp6() { @Test void quadroInCorrectTopic() { - assertThrows(ErtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + assertThrows(EtherScanLogQueryException.class, () -> LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545", "0x72657075746174696f6e00000000000000000000000000000000000000000000", "", From 25751ab85f0dd4d87fb78d3495ac20f8e0dd9da4 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 14 May 2023 22:11:09 +0300 Subject: [PATCH 35/61] [2.0.0-SNAPSHOT] Default converter parsing optimized --- .../goodforgod/api/etherscan/EthScanAPIBuilder.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java b/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java index 69474d9..57aeeae 100644 --- a/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java +++ b/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java @@ -2,11 +2,15 @@ import com.google.gson.Gson; import io.goodforgod.api.etherscan.error.EtherScanKeyException; +import io.goodforgod.api.etherscan.error.EtherScanParseException; import io.goodforgod.api.etherscan.http.EthHttpClient; import io.goodforgod.api.etherscan.http.impl.UrlEthHttpClient; import io.goodforgod.api.etherscan.manager.RequestQueueManager; import io.goodforgod.api.etherscan.util.BasicUtils; import io.goodforgod.gson.configuration.GsonConfiguration; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.util.function.Supplier; import org.jetbrains.annotations.NotNull; @@ -30,8 +34,11 @@ final class EthScanAPIBuilder implements EtherScanAPI.Builder { @Override public @NotNull T fromJson(byte[] jsonAsByteArray, @NotNull Class type) { - final String jsonAsString = new String(jsonAsByteArray, StandardCharsets.UTF_8); - return gson.fromJson(jsonAsString, type); + try (InputStreamReader isr = new InputStreamReader(new ByteArrayInputStream(jsonAsByteArray))) { + return gson.fromJson(isr, type); + } catch (IOException e) { + throw new EtherScanParseException(e.getMessage(), e, new String(jsonAsByteArray, StandardCharsets.UTF_8)); + } } }; From 47e04a832fb504c4e7469d77cf18beb865820b55 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 14 May 2023 22:41:24 +0300 Subject: [PATCH 36/61] [2.0.0-SNAPSHOT] EthSupply for StatisticAPI#supplyTotal added Javadoc fixed --- .../goodforgod/api/etherscan/AccountAPI.java | 2 +- .../api/etherscan/AccountAPIProvider.java | 6 +- .../io/goodforgod/api/etherscan/BlockAPI.java | 2 +- .../goodforgod/api/etherscan/ContractAPI.java | 2 +- .../io/goodforgod/api/etherscan/LogsAPI.java | 2 +- .../io/goodforgod/api/etherscan/ProxyAPI.java | 3 +- .../api/etherscan/ProxyAPIProvider.java | 8 +- .../api/etherscan/StatisticAPI.java | 29 +++-- .../api/etherscan/StatisticAPIProvider.java | 24 +++- .../api/etherscan/TransactionAPI.java | 2 +- .../api/etherscan/model/EthSupply.java | 110 ++++++++++++++++++ .../api/etherscan/model/GasOracle.java | 6 +- .../api/etherscan/model/Supply.java | 18 --- .../goodforgod/api/etherscan/model/Wei.java | 40 ++++--- .../model/response/EthSupplyResponseTO.java | 16 +++ .../gastracker/GasTrackerApiTests.java | 2 +- .../etherscan/model/ModelBuilderTests.java | 8 +- .../statistic/StatisticPriceApiTests.java | 2 +- .../statistic/StatisticSupplyApiTests.java | 6 +- .../StatisticSupplyTotalApiTests.java | 28 +++++ .../StatisticTokenSupplyApiTests.java | 7 +- 21 files changed, 250 insertions(+), 73 deletions(-) create mode 100644 src/main/java/io/goodforgod/api/etherscan/model/EthSupply.java delete mode 100644 src/main/java/io/goodforgod/api/etherscan/model/Supply.java create mode 100644 src/main/java/io/goodforgod/api/etherscan/model/response/EthSupplyResponseTO.java create mode 100644 src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyTotalApiTests.java diff --git a/src/main/java/io/goodforgod/api/etherscan/AccountAPI.java b/src/main/java/io/goodforgod/api/etherscan/AccountAPI.java index 294fb2a..45be8b8 100644 --- a/src/main/java/io/goodforgod/api/etherscan/AccountAPI.java +++ b/src/main/java/io/goodforgod/api/etherscan/AccountAPI.java @@ -6,7 +6,7 @@ import org.jetbrains.annotations.NotNull; /** - * EtherScan - API Descriptions ... + * EtherScan - API Descriptions ... * * @author GoodforGod * @since 28.10.2018 diff --git a/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java index 11bb192..e5b6bd9 100644 --- a/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java @@ -64,7 +64,7 @@ public Balance balance(String address) throws EtherScanException { if (response.getStatus() != 1) throw new EtherScanResponseException(response); - return new Balance(address, new Wei(new BigInteger(response.getResult()))); + return new Balance(address, Wei.ofWei(new BigInteger(response.getResult()))); } @NotNull @@ -78,7 +78,7 @@ public TokenBalance balance(String address, String contract) throws EtherScanExc if (response.getStatus() != 1) throw new EtherScanResponseException(response); - return new TokenBalance(address, new Wei(new BigInteger(response.getResult())), contract); + return new TokenBalance(address, Wei.ofWei(new BigInteger(response.getResult())), contract); } @NotNull @@ -101,7 +101,7 @@ public List balances(List addresses) throws EtherScanException if (!BasicUtils.isEmpty(response.getResult())) balances.addAll(response.getResult().stream() - .map(r -> new Balance(r.getAccount(), new Wei(new BigInteger(r.getBalance())))) + .map(r -> new Balance(r.getAccount(), Wei.ofWei(new BigInteger(r.getBalance())))) .collect(Collectors.toList())); } diff --git a/src/main/java/io/goodforgod/api/etherscan/BlockAPI.java b/src/main/java/io/goodforgod/api/etherscan/BlockAPI.java index 55a8c3b..fdacaf5 100644 --- a/src/main/java/io/goodforgod/api/etherscan/BlockAPI.java +++ b/src/main/java/io/goodforgod/api/etherscan/BlockAPI.java @@ -6,7 +6,7 @@ import org.jetbrains.annotations.NotNull; /** - * EtherScan - API Descriptions ... + * EtherScan - API Descriptions ... * * @author GoodforGod * @since 30.10.2018 diff --git a/src/main/java/io/goodforgod/api/etherscan/ContractAPI.java b/src/main/java/io/goodforgod/api/etherscan/ContractAPI.java index 9271347..7564c98 100644 --- a/src/main/java/io/goodforgod/api/etherscan/ContractAPI.java +++ b/src/main/java/io/goodforgod/api/etherscan/ContractAPI.java @@ -5,7 +5,7 @@ import org.jetbrains.annotations.NotNull; /** - * EtherScan - API Descriptions ... + * EtherScan - API Descriptions ... * * @author GoodforGod * @since 28.10.2018 diff --git a/src/main/java/io/goodforgod/api/etherscan/LogsAPI.java b/src/main/java/io/goodforgod/api/etherscan/LogsAPI.java index 5b834df..01d79f7 100644 --- a/src/main/java/io/goodforgod/api/etherscan/LogsAPI.java +++ b/src/main/java/io/goodforgod/api/etherscan/LogsAPI.java @@ -7,7 +7,7 @@ import org.jetbrains.annotations.NotNull; /** - * EtherScan - API Descriptions ... + * EtherScan - API Descriptions ... * * @author GoodforGod * @since 30.10.2018 diff --git a/src/main/java/io/goodforgod/api/etherscan/ProxyAPI.java b/src/main/java/io/goodforgod/api/etherscan/ProxyAPI.java index b379290..77d6769 100644 --- a/src/main/java/io/goodforgod/api/etherscan/ProxyAPI.java +++ b/src/main/java/io/goodforgod/api/etherscan/ProxyAPI.java @@ -10,7 +10,8 @@ import org.jetbrains.annotations.NotNull; /** - * EtherScan - API Descriptions ... + * EtherScan - API Descriptions + * ... * * @author GoodforGod * @since 30.10.2018 diff --git a/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java index a33f7c1..18edd90 100644 --- a/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java @@ -208,8 +208,8 @@ public Optional storageAt(String address, long position) throws EtherSca public Wei gasPrice() throws EtherScanException { final StringProxyTO response = getRequest(ACT_GASPRICE_PARAM, StringProxyTO.class); return (BasicUtils.isEmpty(response.getResult())) - ? new Wei(0) - : new Wei(BasicUtils.parseHex(response.getResult())); + ? Wei.ofWei(0) + : Wei.ofWei(BasicUtils.parseHex(response.getResult())); } @NotNull @@ -227,7 +227,7 @@ public Wei gasEstimated(String hexData) throws EtherScanException { final String urlParams = ACT_ESTIMATEGAS_PARAM + DATA_PARAM + hexData + GAS_PARAM + "2000000000000000"; final StringProxyTO response = getRequest(urlParams, StringProxyTO.class); return (BasicUtils.isEmpty(response.getResult())) - ? new Wei(0) - : new Wei(BasicUtils.parseHex(response.getResult())); + ? Wei.ofWei(0) + : Wei.ofWei(BasicUtils.parseHex(response.getResult())); } } diff --git a/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java b/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java index 314f73e..10e41e3 100644 --- a/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java +++ b/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java @@ -1,13 +1,13 @@ package io.goodforgod.api.etherscan; import io.goodforgod.api.etherscan.error.EtherScanException; +import io.goodforgod.api.etherscan.model.EthSupply; import io.goodforgod.api.etherscan.model.Price; -import io.goodforgod.api.etherscan.model.Supply; -import java.math.BigInteger; +import io.goodforgod.api.etherscan.model.Wei; import org.jetbrains.annotations.NotNull; /** - * EtherScan - API Descriptions ... + * EtherScan - API Descriptions ... * * @author GoodforGod * @since 30.10.2018 @@ -16,22 +16,35 @@ public interface StatisticAPI { /** * ERC20 token total Supply - * + * EtherScan + * * @param contract contract address * @return token supply for specified contract * @throws EtherScanException parent exception class */ @NotNull - BigInteger supply(String contract) throws EtherScanException; + Wei supply(String contract) throws EtherScanException; /** - * Eth total supply + * Returns the current amount of Ether in circulation excluding ETH2 Staking rewards and EIP1559 + * burnt fees. * * @return total ETH supply for moment * @throws EtherScanException parent exception class */ @NotNull - Supply supply() throws EtherScanException; + Wei supply() throws EtherScanException; + + /** + * Returns the current amount of Ether in circulation, ETH2 Staking rewards, EIP1559 burnt fees, and + * total withdrawn ETH from the beacon chain. + * + * @return total ETH supply for moment + * @throws EtherScanException parent exception class + */ + @NotNull + EthSupply supplyTotal() throws EtherScanException; /** * Eth last USD and BTC price @@ -40,5 +53,5 @@ public interface StatisticAPI { * @throws EtherScanException parent exception class */ @NotNull - Price lastPrice() throws EtherScanException; + Price priceLast() throws EtherScanException; } diff --git a/src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java index 1d1bcee..9555169 100644 --- a/src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java @@ -4,8 +4,10 @@ import io.goodforgod.api.etherscan.error.EtherScanResponseException; import io.goodforgod.api.etherscan.http.EthHttpClient; import io.goodforgod.api.etherscan.manager.RequestQueueManager; +import io.goodforgod.api.etherscan.model.EthSupply; import io.goodforgod.api.etherscan.model.Price; -import io.goodforgod.api.etherscan.model.Supply; +import io.goodforgod.api.etherscan.model.Wei; +import io.goodforgod.api.etherscan.model.response.EthSupplyResponseTO; import io.goodforgod.api.etherscan.model.response.PriceResponseTO; import io.goodforgod.api.etherscan.model.response.StringResponseTO; import io.goodforgod.api.etherscan.util.BasicUtils; @@ -22,6 +24,7 @@ final class StatisticAPIProvider extends BasicProvider implements StatisticAPI { private static final String ACT_SUPPLY_PARAM = ACT_PREFIX + "ethsupply"; + private static final String ACT_SUPPLY2_PARAM = ACT_PREFIX + "ethsupply2"; private static final String ACT_TOKEN_SUPPLY_PARAM = ACT_PREFIX + "tokensupply"; private static final String ACT_LASTPRICE_PARAM = ACT_PREFIX + "ethprice"; @@ -36,17 +39,26 @@ final class StatisticAPIProvider extends BasicProvider implements StatisticAPI { @NotNull @Override - public Supply supply() throws EtherScanException { + public Wei supply() throws EtherScanException { final StringResponseTO response = getRequest(ACT_SUPPLY_PARAM, StringResponseTO.class); if (response.getStatus() != 1) throw new EtherScanResponseException(response); - return new Supply(new BigInteger(response.getResult())); + return Wei.ofWei(new BigInteger(response.getResult())); + } + + @Override + public @NotNull EthSupply supplyTotal() throws EtherScanException { + final EthSupplyResponseTO response = getRequest(ACT_SUPPLY2_PARAM, EthSupplyResponseTO.class); + if (response.getStatus() != 1) + throw new EtherScanResponseException(response); + + return response.getResult(); } @NotNull @Override - public BigInteger supply(String contract) throws EtherScanException { + public Wei supply(String contract) throws EtherScanException { BasicUtils.validateAddress(contract); final String urlParams = ACT_TOKEN_SUPPLY_PARAM + CONTRACT_ADDRESS_PARAM + contract; @@ -54,12 +66,12 @@ public BigInteger supply(String contract) throws EtherScanException { if (response.getStatus() != 1) throw new EtherScanResponseException(response); - return new BigInteger(response.getResult()); + return Wei.ofWei(new BigInteger(response.getResult())); } @NotNull @Override - public Price lastPrice() throws EtherScanException { + public Price priceLast() throws EtherScanException { final PriceResponseTO response = getRequest(ACT_LASTPRICE_PARAM, PriceResponseTO.class); if (response.getStatus() != 1) throw new EtherScanResponseException(response); diff --git a/src/main/java/io/goodforgod/api/etherscan/TransactionAPI.java b/src/main/java/io/goodforgod/api/etherscan/TransactionAPI.java index 6bfc545..a89a4a6 100644 --- a/src/main/java/io/goodforgod/api/etherscan/TransactionAPI.java +++ b/src/main/java/io/goodforgod/api/etherscan/TransactionAPI.java @@ -6,7 +6,7 @@ import org.jetbrains.annotations.NotNull; /** - * EtherScan - API Descriptions ... + * EtherScan - API Descriptions ... * * @author GoodforGod * @since 30.10.2018 diff --git a/src/main/java/io/goodforgod/api/etherscan/model/EthSupply.java b/src/main/java/io/goodforgod/api/etherscan/model/EthSupply.java new file mode 100644 index 0000000..f967360 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/EthSupply.java @@ -0,0 +1,110 @@ +package io.goodforgod.api.etherscan.model; + +import java.math.BigInteger; +import java.util.Objects; + +/** + * Please Add Description Here. + * + * @author Anton Kurako (GoodforGod) + * @since 14.05.2023 + */ +public class EthSupply { + + private String EthSupply; + private String Eth2Staking; + private String BurntFees; + private String WithdrawnTotal; + + public Wei getEthSupply() { + return Wei.ofWei(new BigInteger(EthSupply)); + } + + public Wei getEth2Staking() { + return Wei.ofWei(new BigInteger(Eth2Staking)); + } + + public Wei getBurntFees() { + return Wei.ofWei(new BigInteger(BurntFees)); + } + + public Wei getTotal() { + final BigInteger total = getEthSupply().asWei() + .add(getEth2Staking().asWei()) + .min(getBurntFees().asWei()); + return Wei.ofWei(total); + } + + public Wei getWithdrawnTotal() { + return Wei.ofWei(new BigInteger(WithdrawnTotal)); + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof EthSupply)) + return false; + EthSupply ethSupply = (EthSupply) o; + return Objects.equals(EthSupply, ethSupply.EthSupply) && Objects.equals(Eth2Staking, ethSupply.Eth2Staking) + && Objects.equals(BurntFees, ethSupply.BurntFees) && Objects.equals(WithdrawnTotal, ethSupply.WithdrawnTotal); + } + + @Override + public int hashCode() { + return Objects.hash(EthSupply, Eth2Staking, BurntFees, WithdrawnTotal); + } + + @Override + public String toString() { + return "EthSupply{" + + "EthSupply='" + EthSupply + '\'' + + ", Eth2Staking='" + Eth2Staking + '\'' + + ", BurntFees='" + BurntFees + '\'' + + ", WithdrawnTotal='" + WithdrawnTotal + '\'' + + '}'; + } + + public static EthSupplyBuilder builder() { + return new EthSupplyBuilder(); + } + + public static final class EthSupplyBuilder { + + private Wei ethSupply; + private Wei eth2Staking; + private Wei burntFees; + private Wei withdrawnTotal; + + private EthSupplyBuilder() {} + + public EthSupplyBuilder withEthSupply(Wei ethSupply) { + this.ethSupply = ethSupply; + return this; + } + + public EthSupplyBuilder withEth2Staking(Wei eth2Staking) { + this.eth2Staking = eth2Staking; + return this; + } + + public EthSupplyBuilder withBurntFees(Wei burntFees) { + this.burntFees = burntFees; + return this; + } + + public EthSupplyBuilder withWithdrawnTotal(Wei withdrawnTotal) { + this.withdrawnTotal = withdrawnTotal; + return this; + } + + public EthSupply build() { + EthSupply ethSupply = new EthSupply(); + ethSupply.BurntFees = this.burntFees.toString(); + ethSupply.Eth2Staking = this.eth2Staking.toString(); + ethSupply.EthSupply = this.ethSupply.toString(); + ethSupply.WithdrawnTotal = this.withdrawnTotal.toString(); + return ethSupply; + } + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java b/src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java index 67dd82a..d273357 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java @@ -27,15 +27,15 @@ public Long getLastBlock() { } public Wei getSafeGasPriceInWei() { - return new Wei(BigInteger.valueOf(SafeGasPrice).multiply(BigInteger.TEN.pow(9))); + return Wei.ofWei(BigInteger.valueOf(SafeGasPrice).multiply(BigInteger.TEN.pow(9))); } public Wei getProposeGasPriceInWei() { - return new Wei(BigInteger.valueOf(ProposeGasPrice).multiply(BigInteger.TEN.pow(9))); + return Wei.ofWei(BigInteger.valueOf(ProposeGasPrice).multiply(BigInteger.TEN.pow(9))); } public Wei getFastGasPriceInWei() { - return new Wei(BigInteger.valueOf(FastGasPrice).multiply(BigInteger.TEN.pow(9))); + return Wei.ofWei(BigInteger.valueOf(FastGasPrice).multiply(BigInteger.TEN.pow(9))); } public Double getSuggestBaseFee() { diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Supply.java b/src/main/java/io/goodforgod/api/etherscan/model/Supply.java deleted file mode 100644 index 43e3a3f..0000000 --- a/src/main/java/io/goodforgod/api/etherscan/model/Supply.java +++ /dev/null @@ -1,18 +0,0 @@ -package io.goodforgod.api.etherscan.model; - -import java.math.BigInteger; - -/** - * @author GoodforGod - * @since 30.10.2018 - */ -public class Supply extends Wei { - - public Supply(long value) { - super(value); - } - - public Supply(BigInteger value) { - super(value); - } -} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Wei.java b/src/main/java/io/goodforgod/api/etherscan/model/Wei.java index e23ea51..004b5e1 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Wei.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Wei.java @@ -11,16 +11,32 @@ public class Wei { private final BigInteger result; - public Wei(int value) { - this.result = BigInteger.valueOf(value); + private Wei(BigInteger value) { + this.result = value; } - public Wei(long value) { - this.result = BigInteger.valueOf(value); + public static Wei ofWei(int value) { + return new Wei(BigInteger.valueOf(value)); } - public Wei(BigInteger value) { - this.result = value; + public static Wei ofWei(long value) { + return new Wei(BigInteger.valueOf(value)); + } + + public static Wei ofWei(BigInteger value) { + return new Wei(value); + } + + public static Wei ofEther(int value) { + return new Wei(BigInteger.valueOf(value).multiply(BigInteger.valueOf(1_000_000_000_000_000L))); + } + + public static Wei ofEther(long value) { + return new Wei(BigInteger.valueOf(value).multiply(BigInteger.valueOf(1_000_000_000_000_000L))); + } + + public static Wei ofEther(BigInteger value) { + return new Wei(value.multiply(BigInteger.valueOf(1_000_000_000_000_000L))); } // @@ -29,19 +45,19 @@ public BigInteger asWei() { } public BigInteger asKwei() { - return result.divide(BigInteger.valueOf(1000)); + return result.divide(BigInteger.valueOf(1_000)); } public BigInteger asMwei() { - return result.divide(BigInteger.valueOf(1000000)); + return result.divide(BigInteger.valueOf(1_000_000)); } public BigInteger asGwei() { - return result.divide(BigInteger.valueOf(1000000000)); + return result.divide(BigInteger.valueOf(1_000_000_000)); } public BigInteger asEther() { - return result.divide(BigInteger.valueOf(1000000000000000L)); + return result.divide(BigInteger.valueOf(1_000_000_000_000_000L)); } // @@ -62,8 +78,6 @@ public int hashCode() { @Override public String toString() { - return "Wei{" + - "result=" + result + - '}'; + return result.toString(); } } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/EthSupplyResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/EthSupplyResponseTO.java new file mode 100644 index 0000000..edbc2e3 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/EthSupplyResponseTO.java @@ -0,0 +1,16 @@ +package io.goodforgod.api.etherscan.model.response; + +import io.goodforgod.api.etherscan.model.EthSupply; + +/** + * @author GoodforGod + * @since 14.05.2023 + */ +public class EthSupplyResponseTO extends BaseResponseTO { + + private EthSupply result; + + public EthSupply getResult() { + return result; + } +} diff --git a/src/test/java/io/goodforgod/api/etherscan/gastracker/GasTrackerApiTests.java b/src/test/java/io/goodforgod/api/etherscan/gastracker/GasTrackerApiTests.java index 1d92eb4..53b1c2c 100644 --- a/src/test/java/io/goodforgod/api/etherscan/gastracker/GasTrackerApiTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/gastracker/GasTrackerApiTests.java @@ -14,7 +14,7 @@ class GasTrackerApiTests extends ApiRunner { @Test void estimate() { - GasEstimate estimate = getApi().gasTracker().estimate(new Wei(123)); + GasEstimate estimate = getApi().gasTracker().estimate(Wei.ofWei(123)); assertNotNull(estimate); assertNotNull(estimate.getDuration()); } diff --git a/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java b/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java index 1bec491..9a7e426 100644 --- a/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java @@ -65,16 +65,16 @@ void blockUncleBuilder() { @Test void gasOracleBuilder() { GasOracle value = GasOracle.builder() - .withFastGasPrice(new Wei(1000000000)) - .withProposeGasPrice(new Wei(1000000000)) - .withSafeGasPrice(new Wei(1000000000)) + .withFastGasPrice(Wei.ofWei(1000000000)) + .withProposeGasPrice(Wei.ofWei(1000000000)) + .withSafeGasPrice(Wei.ofWei(1000000000)) .withGasUsedRatio(Collections.singletonList(new BigDecimal(1))) .withLastBlock(1L) .withSuggestBaseFee(1.0) .build(); assertNotNull(value); - assertEquals(new Wei(1000000000), value.getFastGasPriceInWei()); + assertEquals(Wei.ofWei(1000000000), value.getFastGasPriceInWei()); } @Test diff --git a/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTests.java b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTests.java index 3525e21..0dd89c2 100644 --- a/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTests.java @@ -12,7 +12,7 @@ class StatisticPriceApiTests extends ApiRunner { @Test void correct() { - Price price = getApi().stats().lastPrice(); + Price price = getApi().stats().priceLast(); assertNotNull(price); assertNotNull(price.btcTimestamp()); assertNotNull(price.usdTimestamp()); diff --git a/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyApiTests.java b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyApiTests.java index 56469a9..6564c93 100644 --- a/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyApiTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyApiTests.java @@ -1,7 +1,7 @@ package io.goodforgod.api.etherscan.statistic; import io.goodforgod.api.etherscan.ApiRunner; -import io.goodforgod.api.etherscan.model.Supply; +import io.goodforgod.api.etherscan.model.Wei; import java.math.BigInteger; import org.junit.jupiter.api.Test; @@ -13,7 +13,7 @@ class StatisticSupplyApiTests extends ApiRunner { @Test void correct() { - Supply supply = getApi().stats().supply(); + Wei supply = getApi().stats().supply(); assertNotNull(supply); assertNotNull(supply.asWei()); assertNotNull(supply.asGwei()); @@ -22,7 +22,7 @@ void correct() { assertNotNull(supply.asEther()); assertNotNull(supply.toString()); - Supply empty = new Supply(BigInteger.ONE); + Wei empty = Wei.ofWei(BigInteger.ONE); assertNotEquals(supply, empty); assertNotEquals(supply.hashCode(), empty.hashCode()); } diff --git a/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyTotalApiTests.java b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyTotalApiTests.java new file mode 100644 index 0000000..b6098d8 --- /dev/null +++ b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticSupplyTotalApiTests.java @@ -0,0 +1,28 @@ +package io.goodforgod.api.etherscan.statistic; + +import io.goodforgod.api.etherscan.ApiRunner; +import io.goodforgod.api.etherscan.model.EthSupply; +import org.junit.jupiter.api.Test; + +/** + * @author GoodforGod + * @since 14.05.2023 + */ +class StatisticSupplyTotalApiTests extends ApiRunner { + + @Test + void correct() { + EthSupply supply = getApi().stats().supplyTotal(); + assertNotNull(supply); + assertNotNull(supply.getBurntFees()); + assertNotEquals(0, supply.getBurntFees().asWei().intValue()); + assertNotNull(supply.getEthSupply()); + assertNotEquals(0, supply.getEthSupply().asWei().intValue()); + assertNotNull(supply.getEth2Staking()); + assertNotEquals(0, supply.getEth2Staking().asWei().intValue()); + assertNotNull(supply.getWithdrawnTotal()); + assertNotEquals(0, supply.getWithdrawnTotal().asWei().intValue()); + assertNotNull(supply.getTotal()); + assertNotEquals(0, supply.getTotal().asWei().intValue()); + } +} diff --git a/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticTokenSupplyApiTests.java b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticTokenSupplyApiTests.java index 07f8eca..b21b3b3 100644 --- a/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticTokenSupplyApiTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticTokenSupplyApiTests.java @@ -2,6 +2,7 @@ import io.goodforgod.api.etherscan.ApiRunner; import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; +import io.goodforgod.api.etherscan.model.Wei; import java.math.BigInteger; import org.junit.jupiter.api.Test; @@ -13,7 +14,7 @@ class StatisticTokenSupplyApiTests extends ApiRunner { @Test void correct() { - BigInteger supply = getApi().stats().supply("0x57d90b64a1a57749b0f932f1a3395792e12e7055"); + Wei supply = getApi().stats().supply("0x57d90b64a1a57749b0f932f1a3395792e12e7055"); assertNotNull(supply); assertNotEquals(BigInteger.ZERO, supply); } @@ -26,8 +27,8 @@ void invalidParamWithError() { @Test void correctParamWithEmptyExpectedResult() { - BigInteger supply = getApi().stats().supply("0x51d90b64a1a57749b0f932f1a3395792e12e7055"); + Wei supply = getApi().stats().supply("0x51d90b64a1a57749b0f932f1a3395792e12e7055"); assertNotNull(supply); - assertEquals(0, supply.intValue()); + assertEquals(0, supply.asEther().intValue()); } } From 63f8909788f6bb48f91e5451bff71a9eda9e5ab3 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Sun, 14 May 2023 23:12:27 +0300 Subject: [PATCH 37/61] [2.0.0-SNAPSHOT] 1010L->1015L reset time --- .../api/etherscan/manager/RequestQueueManager.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java b/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java index 2fdfe82..4d6b586 100644 --- a/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java +++ b/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java @@ -16,16 +16,16 @@ public interface RequestQueueManager extends AutoCloseable { /** * Is used by default when no API KEY is provided */ - RequestQueueManager ANONYMOUS = new SemaphoreRequestQueueManager(1, Duration.ofMillis(5010L)); + RequestQueueManager ANONYMOUS = new SemaphoreRequestQueueManager(1, Duration.ofMillis(5015L)); /** * Is available for all registered free API KEYs * Free API KEY */ - RequestQueueManager FREE_PLAN = new SemaphoreRequestQueueManager(5, Duration.ofMillis(1010L)); - RequestQueueManager STANDARD_PLAN = new SemaphoreRequestQueueManager(10, Duration.ofMillis(1010L)); - RequestQueueManager ADVANCED_PLAN = new SemaphoreRequestQueueManager(20, Duration.ofMillis(1010L)); - RequestQueueManager PROFESSIONAL_PLAN = new SemaphoreRequestQueueManager(30, Duration.ofMillis(1010L)); + RequestQueueManager FREE_PLAN = new SemaphoreRequestQueueManager(5, Duration.ofMillis(1015L)); + RequestQueueManager STANDARD_PLAN = new SemaphoreRequestQueueManager(10, Duration.ofMillis(1015L)); + RequestQueueManager ADVANCED_PLAN = new SemaphoreRequestQueueManager(20, Duration.ofMillis(1015L)); + RequestQueueManager PROFESSIONAL_PLAN = new SemaphoreRequestQueueManager(30, Duration.ofMillis(1015L)); RequestQueueManager UNLIMITED = new FakeRequestQueueManager(); From 6d19b737db506844f4aa821f71a37f0bfdf9ca8e Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Mon, 15 May 2023 00:05:46 +0300 Subject: [PATCH 38/61] [2.0.0-SNAPSHOT] Gas related fields replaced to Wei --- .../api/etherscan/model/BaseTx.java | 8 +- .../io/goodforgod/api/etherscan/model/Tx.java | 50 +++++++----- .../api/etherscan/model/TxErc1155.java | 76 ++++++++++--------- .../api/etherscan/model/TxErc20.java | 40 ++++++---- .../api/etherscan/model/TxErc721.java | 40 ++++++---- .../api/etherscan/model/TxInternal.java | 16 ++-- .../goodforgod/api/etherscan/model/Wei.java | 22 ++++-- .../api/etherscan/model/proxy/BlockProxy.java | 24 +++--- .../etherscan/model/proxy/ReceiptProxy.java | 24 +++--- .../api/etherscan/model/proxy/TxProxy.java | 24 +++--- 10 files changed, 180 insertions(+), 144 deletions(-) diff --git a/src/main/java/io/goodforgod/api/etherscan/model/BaseTx.java b/src/main/java/io/goodforgod/api/etherscan/model/BaseTx.java index c66e60f..64a9627 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/BaseTx.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/BaseTx.java @@ -56,12 +56,12 @@ public String getInput() { return input; } - public BigInteger getGas() { - return gas; + public Wei getGas() { + return Wei.ofWei(gas); } - public BigInteger getGasUsed() { - return gasUsed; + public Wei getGasUsed() { + return Wei.ofWei(gasUsed); } // diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Tx.java b/src/main/java/io/goodforgod/api/etherscan/model/Tx.java index 3d8cd1f..819252e 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Tx.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Tx.java @@ -41,20 +41,20 @@ public int getTransactionIndex() { return transactionIndex; } - public BigInteger getGasPrice() { - return gasPrice; + public Wei getGasPrice() { + return Wei.ofWei(gasPrice); } public boolean haveError() { return !BasicUtils.isEmpty(isError) && !isError.equals("0"); } - public String getTxreceipt_status() { + public String getTxReceiptStatus() { return txreceipt_status; } - public BigInteger getCumulativeGasUsed() { - return cumulativeGasUsed; + public Wei getGasUsedCumulative() { + return Wei.ofWei(cumulativeGasUsed); } public long getConfirmations() { @@ -112,16 +112,16 @@ public static final class TxBuilder { private BigInteger value; private String contractAddress; private String input; - private BigInteger gas; - private BigInteger gasUsed; + private Wei gas; + private Wei gasUsed; private long nonce; private String blockHash; private int transactionIndex; - private BigInteger gasPrice; - private BigInteger cumulativeGasUsed; + private Wei gasPrice; + private Wei cumulativeGasUsed; private long confirmations; private String isError; - private String txreceiptStatus; + private String txReceiptStatus; private TxBuilder() {} @@ -165,12 +165,12 @@ public TxBuilder withInput(String input) { return this; } - public TxBuilder withGas(BigInteger gas) { + public TxBuilder withGas(Wei gas) { this.gas = gas; return this; } - public TxBuilder withGasUsed(BigInteger gasUsed) { + public TxBuilder withGasUsed(Wei gasUsed) { this.gasUsed = gasUsed; return this; } @@ -190,12 +190,12 @@ public TxBuilder withTransactionIndex(int transactionIndex) { return this; } - public TxBuilder withGasPrice(BigInteger gasPrice) { + public TxBuilder withGasPrice(Wei gasPrice) { this.gasPrice = gasPrice; return this; } - public TxBuilder withCumulativeGasUsed(BigInteger cumulativeGasUsed) { + public TxBuilder withCumulativeGasUsed(Wei cumulativeGasUsed) { this.cumulativeGasUsed = cumulativeGasUsed; return this; } @@ -210,20 +210,30 @@ public TxBuilder withIsError(String isError) { return this; } - public TxBuilder withTxreceiptStatus(String txreceiptStatus) { - this.txreceiptStatus = txreceiptStatus; + public TxBuilder withTxReceiptStatus(String txReceiptStatus) { + this.txReceiptStatus = txReceiptStatus; return this; } public Tx build() { Tx tx = new Tx(); - tx.gas = this.gas; tx.isError = this.isError; tx.blockHash = this.blockHash; tx.hash = this.hash; - tx.gasUsed = this.gasUsed; + if (this.gas != null) { + tx.gas = this.gas.asWei(); + } + if (this.gasUsed != null) { + tx.gasUsed = this.gasUsed.asWei(); + } + if (this.gasPrice != null) { + tx.gasPrice = this.gasPrice.asWei(); + } + if (this.cumulativeGasUsed != null) { + tx.cumulativeGasUsed = this.cumulativeGasUsed.asWei(); + } tx.from = this.from; - tx.txreceipt_status = this.txreceiptStatus; + tx.txreceipt_status = this.txReceiptStatus; tx.contractAddress = this.contractAddress; tx.value = this.value; tx.transactionIndex = this.transactionIndex; @@ -236,8 +246,6 @@ public Tx build() { tx.blockNumber = this.blockNumber; tx.to = this.to; tx.input = this.input; - tx.cumulativeGasUsed = this.cumulativeGasUsed; - tx.gasPrice = this.gasPrice; return tx; } } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java b/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java index e6c20f0..7be8aff 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java @@ -53,12 +53,12 @@ public int getTransactionIndex() { return transactionIndex; } - public BigInteger getGasPrice() { - return gasPrice; + public Wei getGasPrice() { + return Wei.ofWei(gasPrice); } - public BigInteger getCumulativeGasUsed() { - return cumulativeGasUsed; + public Wei getGasUsedCumulative() { + return Wei.ofWei(cumulativeGasUsed); } public long getConfirmations() { @@ -113,8 +113,6 @@ public static final class TxErc1155Builder { private String to; private String contractAddress; private String input; - private BigInteger gas; - private BigInteger gasUsed; private long nonce; private String blockHash; private String tokenID; @@ -122,8 +120,10 @@ public static final class TxErc1155Builder { private String tokenSymbol; private String tokenValue; private int transactionIndex; - private BigInteger gasPrice; - private BigInteger cumulativeGasUsed; + private Wei gas; + private Wei gasUsed; + private Wei gasPrice; + private Wei cumulativeGasUsed; private long confirmations; private TxErc1155Builder() {} @@ -163,12 +163,12 @@ public TxErc1155Builder withInput(String input) { return this; } - public TxErc1155Builder withGas(BigInteger gas) { + public TxErc1155Builder withGas(Wei gas) { this.gas = gas; return this; } - public TxErc1155Builder withGasUsed(BigInteger gasUsed) { + public TxErc1155Builder withGasUsed(Wei gasUsed) { this.gasUsed = gasUsed; return this; } @@ -208,12 +208,12 @@ public TxErc1155Builder withTransactionIndex(int transactionIndex) { return this; } - public TxErc1155Builder withGasPrice(BigInteger gasPrice) { + public TxErc1155Builder withGasPrice(Wei gasPrice) { this.gasPrice = gasPrice; return this; } - public TxErc1155Builder withCumulativeGasUsed(BigInteger cumulativeGasUsed) { + public TxErc1155Builder withCumulativeGasUsed(Wei cumulativeGasUsed) { this.cumulativeGasUsed = cumulativeGasUsed; return this; } @@ -224,30 +224,38 @@ public TxErc1155Builder withConfirmations(long confirmations) { } public TxErc1155 build() { - TxErc1155 txERC721 = new TxErc1155(); - txERC721.gas = this.gas; - txERC721.tokenName = this.tokenName; - txERC721.hash = this.hash; - txERC721.gasUsed = this.gasUsed; - txERC721.nonce = this.nonce; - txERC721.from = this.from; - txERC721.gasPrice = this.gasPrice; - txERC721.contractAddress = this.contractAddress; - txERC721.cumulativeGasUsed = this.cumulativeGasUsed; - txERC721.tokenID = this.tokenID; + TxErc1155 txERC1155 = new TxErc1155(); + txERC1155.tokenName = this.tokenName; + txERC1155.hash = this.hash; + txERC1155.nonce = this.nonce; + txERC1155.from = this.from; + if (this.gas != null) { + txERC1155.gas = this.gas.asWei(); + } + if (this.gasUsed != null) { + txERC1155.gasUsed = this.gasUsed.asWei(); + } + if (this.gasPrice != null) { + txERC1155.gasPrice = this.gasPrice.asWei(); + } + if (this.cumulativeGasUsed != null) { + txERC1155.cumulativeGasUsed = this.cumulativeGasUsed.asWei(); + } + txERC1155.contractAddress = this.contractAddress; + txERC1155.tokenID = this.tokenID; if (this.timeStamp != null) { - txERC721.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); - txERC721._timeStamp = this.timeStamp; + txERC1155.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); + txERC1155._timeStamp = this.timeStamp; } - txERC721.blockNumber = this.blockNumber; - txERC721.tokenValue = this.tokenValue; - txERC721.transactionIndex = this.transactionIndex; - txERC721.to = this.to; - txERC721.confirmations = this.confirmations; - txERC721.input = this.input; - txERC721.blockHash = this.blockHash; - txERC721.tokenSymbol = this.tokenSymbol; - return txERC721; + txERC1155.blockNumber = this.blockNumber; + txERC1155.tokenValue = this.tokenValue; + txERC1155.transactionIndex = this.transactionIndex; + txERC1155.to = this.to; + txERC1155.confirmations = this.confirmations; + txERC1155.input = this.input; + txERC1155.blockHash = this.blockHash; + txERC1155.tokenSymbol = this.tokenSymbol; + return txERC1155; } } } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java b/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java index 197ab5d..751044c 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java @@ -53,12 +53,12 @@ public int getTransactionIndex() { return transactionIndex; } - public BigInteger getGasPrice() { - return gasPrice; + public Wei getGasPrice() { + return Wei.ofWei(gasPrice); } - public BigInteger getCumulativeGasUsed() { - return cumulativeGasUsed; + public Wei getGasUsedCumulative() { + return Wei.ofWei(cumulativeGasUsed); } public long getConfirmations() { @@ -114,16 +114,16 @@ public static final class TxERC20Builder { private BigInteger value; private String contractAddress; private String input; - private BigInteger gas; - private BigInteger gasUsed; + private Wei gas; + private Wei gasUsed; private long nonce; private String blockHash; private String tokenName; private String tokenSymbol; private String tokenDecimal; private int transactionIndex; - private BigInteger gasPrice; - private BigInteger cumulativeGasUsed; + private Wei gasPrice; + private Wei cumulativeGasUsed; private long confirmations; private TxERC20Builder() {} @@ -168,12 +168,12 @@ public TxERC20Builder withInput(String input) { return this; } - public TxERC20Builder withGas(BigInteger gas) { + public TxERC20Builder withGas(Wei gas) { this.gas = gas; return this; } - public TxERC20Builder withGasUsed(BigInteger gasUsed) { + public TxERC20Builder withGasUsed(Wei gasUsed) { this.gasUsed = gasUsed; return this; } @@ -208,12 +208,12 @@ public TxERC20Builder withTransactionIndex(int transactionIndex) { return this; } - public TxERC20Builder withGasPrice(BigInteger gasPrice) { + public TxERC20Builder withGasPrice(Wei gasPrice) { this.gasPrice = gasPrice; return this; } - public TxERC20Builder withCumulativeGasUsed(BigInteger cumulativeGasUsed) { + public TxERC20Builder withCumulativeGasUsed(Wei cumulativeGasUsed) { this.cumulativeGasUsed = cumulativeGasUsed; return this; } @@ -225,11 +225,20 @@ public TxERC20Builder withConfirmations(long confirmations) { public TxErc20 build() { TxErc20 txERC20 = new TxErc20(); - txERC20.gas = this.gas; txERC20.tokenName = this.tokenName; txERC20.hash = this.hash; - txERC20.gasUsed = this.gasUsed; - txERC20.cumulativeGasUsed = this.cumulativeGasUsed; + if (this.gas != null) { + txERC20.gas = this.gas.asWei(); + } + if (this.gasUsed != null) { + txERC20.gasUsed = this.gasUsed.asWei(); + } + if (this.gasPrice != null) { + txERC20.gasPrice = this.gasPrice.asWei(); + } + if (this.cumulativeGasUsed != null) { + txERC20.cumulativeGasUsed = this.cumulativeGasUsed.asWei(); + } txERC20.from = this.from; txERC20.tokenSymbol = this.tokenSymbol; txERC20.transactionIndex = this.transactionIndex; @@ -243,7 +252,6 @@ public TxErc20 build() { } txERC20.blockHash = this.blockHash; txERC20.blockNumber = this.blockNumber; - txERC20.gasPrice = this.gasPrice; txERC20.to = this.to; txERC20.input = this.input; txERC20.tokenDecimal = this.tokenDecimal; diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java b/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java index 644f738..7b59393 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java @@ -53,12 +53,12 @@ public int getTransactionIndex() { return transactionIndex; } - public BigInteger getGasPrice() { - return gasPrice; + public Wei getGasPrice() { + return Wei.ofWei(gasPrice); } - public BigInteger getCumulativeGasUsed() { - return cumulativeGasUsed; + public Wei getGasUsedCumulative() { + return Wei.ofWei(cumulativeGasUsed); } public long getConfirmations() { @@ -113,8 +113,6 @@ public static final class TxERC721Builder { private String to; private String contractAddress; private String input; - private BigInteger gas; - private BigInteger gasUsed; private long nonce; private String blockHash; private String tokenID; @@ -122,8 +120,10 @@ public static final class TxERC721Builder { private String tokenSymbol; private String tokenDecimal; private int transactionIndex; - private BigInteger gasPrice; - private BigInteger cumulativeGasUsed; + private Wei gas; + private Wei gasUsed; + private Wei gasPrice; + private Wei cumulativeGasUsed; private long confirmations; private TxERC721Builder() {} @@ -163,12 +163,12 @@ public TxERC721Builder withInput(String input) { return this; } - public TxERC721Builder withGas(BigInteger gas) { + public TxERC721Builder withGas(Wei gas) { this.gas = gas; return this; } - public TxERC721Builder withGasUsed(BigInteger gasUsed) { + public TxERC721Builder withGasUsed(Wei gasUsed) { this.gasUsed = gasUsed; return this; } @@ -208,12 +208,12 @@ public TxERC721Builder withTransactionIndex(int transactionIndex) { return this; } - public TxERC721Builder withGasPrice(BigInteger gasPrice) { + public TxERC721Builder withGasPrice(Wei gasPrice) { this.gasPrice = gasPrice; return this; } - public TxERC721Builder withCumulativeGasUsed(BigInteger cumulativeGasUsed) { + public TxERC721Builder withCumulativeGasUsed(Wei cumulativeGasUsed) { this.cumulativeGasUsed = cumulativeGasUsed; return this; } @@ -225,15 +225,23 @@ public TxERC721Builder withConfirmations(long confirmations) { public TxErc721 build() { TxErc721 txERC721 = new TxErc721(); - txERC721.gas = this.gas; txERC721.tokenName = this.tokenName; txERC721.hash = this.hash; - txERC721.gasUsed = this.gasUsed; txERC721.nonce = this.nonce; txERC721.from = this.from; - txERC721.gasPrice = this.gasPrice; + if (this.gas != null) { + txERC721.gas = this.gas.asWei(); + } + if (this.gasUsed != null) { + txERC721.gasUsed = this.gasUsed.asWei(); + } + if (this.gasPrice != null) { + txERC721.gasPrice = this.gasPrice.asWei(); + } + if (this.cumulativeGasUsed != null) { + txERC721.cumulativeGasUsed = this.cumulativeGasUsed.asWei(); + } txERC721.contractAddress = this.contractAddress; - txERC721.cumulativeGasUsed = this.cumulativeGasUsed; txERC721.tokenID = this.tokenID; if (this.timeStamp != null) { txERC721.timeStamp = String.valueOf(this.timeStamp.toEpochSecond(ZoneOffset.UTC)); diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java b/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java index fdd89ee..dd74e99 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java @@ -90,8 +90,8 @@ public static final class TxInternalBuilder { private BigInteger value; private String contractAddress; private String input; - private BigInteger gas; - private BigInteger gasUsed; + private Wei gas; + private Wei gasUsed; private String type; private String traceId; private int isError; @@ -139,12 +139,12 @@ public TxInternalBuilder withInput(String input) { return this; } - public TxInternalBuilder withGas(BigInteger gas) { + public TxInternalBuilder withGas(Wei gas) { this.gas = gas; return this; } - public TxInternalBuilder withGasUsed(BigInteger gasUsed) { + public TxInternalBuilder withGasUsed(Wei gasUsed) { this.gasUsed = gasUsed; return this; } @@ -171,9 +171,13 @@ public TxInternalBuilder withErrCode(String errCode) { public TxInternal build() { TxInternal txInternal = new TxInternal(); - txInternal.gas = this.gas; txInternal.hash = this.hash; - txInternal.gasUsed = this.gasUsed; + if (this.gas != null) { + txInternal.gas = this.gas.asWei(); + } + if (this.gasUsed != null) { + txInternal.gasUsed = this.gasUsed.asWei(); + } txInternal.traceId = this.traceId; txInternal.type = this.type; txInternal.from = this.from; diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Wei.java b/src/main/java/io/goodforgod/api/etherscan/model/Wei.java index 004b5e1..038fd4b 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Wei.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Wei.java @@ -1,6 +1,8 @@ package io.goodforgod.api.etherscan.model; +import java.math.BigDecimal; import java.math.BigInteger; +import java.math.RoundingMode; import java.util.Objects; /** @@ -39,25 +41,29 @@ public static Wei ofEther(BigInteger value) { return new Wei(value.multiply(BigInteger.valueOf(1_000_000_000_000_000L))); } + public static Wei ofEther(BigDecimal value) { + return new Wei(value.multiply(BigDecimal.valueOf(1_000_000_000_000_000L)).toBigInteger()); + } + // public BigInteger asWei() { return result; } - public BigInteger asKwei() { - return result.divide(BigInteger.valueOf(1_000)); + public BigDecimal asKwei() { + return new BigDecimal(result).divide(BigDecimal.valueOf(1_000), RoundingMode.HALF_UP); } - public BigInteger asMwei() { - return result.divide(BigInteger.valueOf(1_000_000)); + public BigDecimal asMwei() { + return new BigDecimal(result).divide(BigDecimal.valueOf(1_000_000), RoundingMode.HALF_UP); } - public BigInteger asGwei() { - return result.divide(BigInteger.valueOf(1_000_000_000)); + public BigDecimal asGwei() { + return new BigDecimal(result).divide(BigDecimal.valueOf(1_000_000_000), RoundingMode.HALF_UP); } - public BigInteger asEther() { - return result.divide(BigInteger.valueOf(1_000_000_000_000_000L)); + public BigDecimal asEther() { + return new BigDecimal(result).divide(BigDecimal.valueOf(1_000_000_000_000_000L), RoundingMode.HALF_UP); } // diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java index a9447ca..c98d5ee 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java @@ -1,8 +1,8 @@ package io.goodforgod.api.etherscan.model.proxy; import com.google.gson.annotations.Expose; +import io.goodforgod.api.etherscan.model.Wei; import io.goodforgod.api.etherscan.util.BasicUtils; -import java.math.BigInteger; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.List; @@ -35,10 +35,10 @@ public class BlockProxy { private String mixHash; private String gasUsed; @Expose(deserialize = false, serialize = false) - private BigInteger _gasUsed; + private Wei _gasUsed; private String gasLimit; @Expose(deserialize = false, serialize = false) - private BigInteger _gasLimit; + private Wei _gasLimit; private String sha3Uncles; private List uncles; @@ -108,15 +108,15 @@ public String getMixHash() { return mixHash; } - public BigInteger getGasUsed() { + public Wei getGasUsed() { if (_gasUsed == null && !BasicUtils.isEmpty(gasUsed)) - _gasUsed = BasicUtils.parseHex(gasUsed); + _gasUsed = Wei.ofWei(BasicUtils.parseHex(gasUsed)); return _gasUsed; } - public BigInteger getGasLimit() { + public Wei getGasLimit() { if (_gasLimit == null && !BasicUtils.isEmpty(gasLimit)) - _gasLimit = BasicUtils.parseHex(gasLimit); + _gasLimit = Wei.ofWei(BasicUtils.parseHex(gasLimit)); return _gasLimit; } @@ -227,8 +227,8 @@ public static final class BlockProxyBuilder { private String extraData; private String logsBloom; private String mixHash; - private BigInteger gasUsed; - private BigInteger gasLimit; + private Wei gasUsed; + private Wei gasLimit; private String sha3Uncles; private List uncles; private String receiptsRoot; @@ -302,12 +302,12 @@ public BlockProxyBuilder withMixHash(String mixHash) { return this; } - public BlockProxyBuilder withGasUsed(BigInteger gasUsed) { + public BlockProxyBuilder withGasUsed(Wei gasUsed) { this.gasUsed = gasUsed; return this; } - public BlockProxyBuilder withGasLimit(BigInteger gasLimit) { + public BlockProxyBuilder withGasLimit(Wei gasLimit) { this.gasLimit = gasLimit; return this; } @@ -352,11 +352,9 @@ public BlockProxy build() { blockProxy._size = this.size; blockProxy.difficulty = this.difficulty; if (this.gasLimit != null) { - blockProxy.gasLimit = String.valueOf(this.gasLimit); blockProxy._gasLimit = this.gasLimit; } if (this.gasUsed != null) { - blockProxy.gasUsed = String.valueOf(this.gasUsed); blockProxy._gasUsed = this.gasUsed; } blockProxy.size = String.valueOf(this.size); diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java index 61a7942..2b616c3 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java @@ -2,8 +2,8 @@ import com.google.gson.annotations.Expose; import io.goodforgod.api.etherscan.model.Log; +import io.goodforgod.api.etherscan.model.Wei; import io.goodforgod.api.etherscan.util.BasicUtils; -import java.math.BigInteger; import java.util.List; /** @@ -25,10 +25,10 @@ public class ReceiptProxy { private Long _transactionIndex; private String gasUsed; @Expose(serialize = false, deserialize = false) - private BigInteger _gasUsed; + private Wei _gasUsed; private String cumulativeGasUsed; @Expose(serialize = false, deserialize = false) - private BigInteger _cumulativeGasUsed; + private Wei _cumulativeGasUsed; private String contractAddress; private List logs; @@ -69,15 +69,15 @@ public Long getTransactionIndex() { return _transactionIndex; } - public BigInteger getGasUsed() { + public Wei getGasUsed() { if (_gasUsed == null && !BasicUtils.isEmpty(gasUsed)) - _gasUsed = BasicUtils.parseHex(gasUsed); + _gasUsed = Wei.ofWei(BasicUtils.parseHex(gasUsed)); return _gasUsed; } - public BigInteger getCumulativeGasUsed() { + public Wei getCumulativeGasUsed() { if (_cumulativeGasUsed == null && !BasicUtils.isEmpty(cumulativeGasUsed)) - _cumulativeGasUsed = BasicUtils.parseHex(cumulativeGasUsed); + _cumulativeGasUsed = Wei.ofWei(BasicUtils.parseHex(cumulativeGasUsed)); return _cumulativeGasUsed; } @@ -165,8 +165,8 @@ public static final class ReceiptProxyBuilder { private String blockHash; private String transactionHash; private Long transactionIndex; - private BigInteger gasUsed; - private BigInteger cumulativeGasUsed; + private Wei gasUsed; + private Wei cumulativeGasUsed; private String contractAddress; private List logs; private String logsBloom; @@ -208,12 +208,12 @@ public ReceiptProxyBuilder withTransactionIndex(Long transactionIndex) { return this; } - public ReceiptProxyBuilder withGasUsed(BigInteger gasUsed) { + public ReceiptProxyBuilder withGasUsed(Wei gasUsed) { this.gasUsed = gasUsed; return this; } - public ReceiptProxyBuilder withCumulativeGasUsed(BigInteger cumulativeGasUsed) { + public ReceiptProxyBuilder withCumulativeGasUsed(Wei cumulativeGasUsed) { this.cumulativeGasUsed = cumulativeGasUsed; return this; } @@ -244,13 +244,11 @@ public ReceiptProxy build() { receiptProxy.root = this.root; receiptProxy.contractAddress = this.contractAddress; if (this.gasUsed != null) { - receiptProxy.gasUsed = String.valueOf(this.gasUsed); receiptProxy._gasUsed = this.gasUsed; } receiptProxy.logs = this.logs; receiptProxy.to = this.to; if (this.cumulativeGasUsed != null) { - receiptProxy.cumulativeGasUsed = String.valueOf(this.cumulativeGasUsed); receiptProxy._cumulativeGasUsed = this.cumulativeGasUsed; } receiptProxy.transactionIndex = String.valueOf(this.transactionIndex); diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java index 0ca7f3a..b2b412b 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java @@ -1,8 +1,8 @@ package io.goodforgod.api.etherscan.model.proxy; import com.google.gson.annotations.Expose; +import io.goodforgod.api.etherscan.model.Wei; import io.goodforgod.api.etherscan.util.BasicUtils; -import java.math.BigInteger; /** * @author GoodforGod @@ -26,10 +26,10 @@ public class TxProxy { private String value; private String gas; @Expose(deserialize = false, serialize = false) - private BigInteger _gas; + private Wei _gas; private String gasPrice; @Expose(deserialize = false, serialize = false) - private BigInteger _gasPrice; + private Wei _gasPrice; private String blockHash; private String blockNumber; @Expose(deserialize = false, serialize = false) @@ -56,9 +56,9 @@ public String getFrom() { return from; } - public BigInteger getGas() { + public Wei getGas() { if (_gas == null && !BasicUtils.isEmpty(gas)) - _gas = BasicUtils.parseHex(gas); + _gas = Wei.ofWei(BasicUtils.parseHex(gas)); return _gas; } @@ -88,9 +88,9 @@ public String getValue() { return value; } - public BigInteger getGasPrice() { + public Wei getGasPrice() { if (_gasPrice == null && !BasicUtils.isEmpty(gasPrice)) - _gasPrice = BasicUtils.parseHex(gasPrice); + _gasPrice = Wei.ofWei(BasicUtils.parseHex(gasPrice)); return _gasPrice; } @@ -182,8 +182,8 @@ public static final class TxProxyBuilder { private String r; private Long nonce; private String value; - private BigInteger gas; - private BigInteger gasPrice; + private Wei gas; + private Wei gasPrice; private String blockHash; private Long blockNumber; @@ -239,12 +239,12 @@ public TxProxyBuilder withValue(String value) { return this; } - public TxProxyBuilder withGas(BigInteger gas) { + public TxProxyBuilder withGas(Wei gas) { this.gas = gas; return this; } - public TxProxyBuilder withGasPrice(BigInteger gasPrice) { + public TxProxyBuilder withGasPrice(Wei gasPrice) { this.gasPrice = gasPrice; return this; } @@ -263,7 +263,6 @@ public TxProxy build() { TxProxy txProxy = new TxProxy(); txProxy.input = this.input; if (this.gas != null) { - txProxy.gas = String.valueOf(this.gas); txProxy._gas = this.gas; } txProxy.s = this.s; @@ -281,7 +280,6 @@ public TxProxy build() { txProxy._blockNumber = this.blockNumber; txProxy.hash = this.hash; if (this.gasPrice != null) { - txProxy.gasPrice = String.valueOf(this.gasPrice); txProxy._gasPrice = this.gasPrice; } return txProxy; From 1416a232e4a8c13b46e6cd7bf571f063b9bb0da0 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Mon, 15 May 2023 00:06:23 +0300 Subject: [PATCH 39/61] [2.0.0-SNAPSHOT] RequestQueueManager static consts -> static method to produce uniq request queue managers Tests manager provision fixed --- .../api/etherscan/AccountAPIProvider.java | 9 +++-- .../api/etherscan/EthScanAPIBuilder.java | 17 ++++++--- .../manager/RequestQueueManager.java | 27 ++++++++++--- .../goodforgod/api/etherscan/ApiRunner.java | 8 +--- .../api/etherscan/EtherScanAPITests.java | 1 - .../account/AccountTxErc20Tests.java | 4 +- .../account/AccountTxRc1155TokenTests.java | 2 +- .../account/AccountTxRc721TokenTests.java | 2 +- .../etherscan/account/AccountTxsTests.java | 4 +- .../etherscan/model/ModelBuilderTests.java | 38 +++++++++---------- .../etherscan/proxy/ProxyBlockApiTests.java | 17 ++------- 11 files changed, 69 insertions(+), 60 deletions(-) diff --git a/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java index e5b6bd9..08e9dd5 100644 --- a/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java @@ -84,8 +84,9 @@ public TokenBalance balance(String address, String contract) throws EtherScanExc @NotNull @Override public List balances(List addresses) throws EtherScanException { - if (BasicUtils.isEmpty(addresses)) + if (BasicUtils.isEmpty(addresses)) { return Collections.emptyList(); + } BasicUtils.validateAddresses(addresses); @@ -96,13 +97,15 @@ public List balances(List addresses) throws EtherScanException for (final List batch : addressesAsBatches) { final String urlParams = ACT_BALANCE_MULTI_ACTION + TAG_LATEST_PARAM + ADDRESS_PARAM + toAddressParam(batch); final BalanceResponseTO response = getRequest(urlParams, BalanceResponseTO.class); - if (response.getStatus() != 1) + if (response.getStatus() != 1) { throw new EtherScanResponseException(response); + } - if (!BasicUtils.isEmpty(response.getResult())) + if (!BasicUtils.isEmpty(response.getResult())) { balances.addAll(response.getResult().stream() .map(r -> new Balance(r.getAccount(), Wei.ofWei(new BigInteger(r.getBalance())))) .collect(Collectors.toList())); + } } return balances; diff --git a/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java b/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java index 57aeeae..dad9c50 100644 --- a/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java +++ b/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java @@ -27,8 +27,8 @@ final class EthScanAPIBuilder implements EtherScanAPI.Builder { private final Gson gson = new GsonConfiguration().builder().create(); private String apiKey = DEFAULT_KEY; + private RequestQueueManager queueManager; private EthNetwork ethNetwork = EthNetworks.MAINNET; - private RequestQueueManager queueManager = RequestQueueManager.ANONYMOUS; private Supplier ethHttpClientSupplier = DEFAULT_SUPPLIER; private Supplier converterSupplier = () -> new Converter() { @@ -49,9 +49,6 @@ public EtherScanAPI.Builder withApiKey(@NotNull String apiKey) { throw new EtherScanKeyException("API key can not be null or empty"); this.apiKey = apiKey; - if (!DEFAULT_KEY.equals(apiKey)) { - queueManager = RequestQueueManager.UNLIMITED; - } return this; } @@ -92,6 +89,16 @@ public EtherScanAPI.Builder withConverter(@NotNull Supplier converter @Override public @NotNull EtherScanAPI build() { - return new EtherScanAPIProvider(apiKey, ethNetwork, queueManager, ethHttpClientSupplier.get(), converterSupplier.get()); + RequestQueueManager requestQueueManager; + if (queueManager != null) { + requestQueueManager = queueManager; + } else if (DEFAULT_KEY.equals(apiKey)) { + requestQueueManager = RequestQueueManager.anonymous(); + } else { + requestQueueManager = RequestQueueManager.planFree(); + } + + return new EtherScanAPIProvider(apiKey, ethNetwork, requestQueueManager, ethHttpClientSupplier.get(), + converterSupplier.get()); } } diff --git a/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java b/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java index 4d6b586..449daca 100644 --- a/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java +++ b/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java @@ -16,18 +16,33 @@ public interface RequestQueueManager extends AutoCloseable { /** * Is used by default when no API KEY is provided */ - RequestQueueManager ANONYMOUS = new SemaphoreRequestQueueManager(1, Duration.ofMillis(5015L)); + static RequestQueueManager anonymous() { + return new SemaphoreRequestQueueManager(1, Duration.ofMillis(5005L)); + } /** * Is available for all registered free API KEYs * Free API KEY */ - RequestQueueManager FREE_PLAN = new SemaphoreRequestQueueManager(5, Duration.ofMillis(1015L)); - RequestQueueManager STANDARD_PLAN = new SemaphoreRequestQueueManager(10, Duration.ofMillis(1015L)); - RequestQueueManager ADVANCED_PLAN = new SemaphoreRequestQueueManager(20, Duration.ofMillis(1015L)); - RequestQueueManager PROFESSIONAL_PLAN = new SemaphoreRequestQueueManager(30, Duration.ofMillis(1015L)); + static RequestQueueManager planFree() { + return new SemaphoreRequestQueueManager(5, Duration.ofMillis(1005L)); + } - RequestQueueManager UNLIMITED = new FakeRequestQueueManager(); + static RequestQueueManager planStandard() { + return new SemaphoreRequestQueueManager(10, Duration.ofMillis(1005L)); + } + + static RequestQueueManager planAdvanced() { + return new SemaphoreRequestQueueManager(20, Duration.ofMillis(1005L)); + } + + static RequestQueueManager planProfessional() { + return new SemaphoreRequestQueueManager(30, Duration.ofMillis(1005L)); + } + + static RequestQueueManager unlimited() { + return new FakeRequestQueueManager(); + } /** * Waits in queue for chance to take turn diff --git a/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java b/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java index d28a8e0..4b52c00 100644 --- a/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java +++ b/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java @@ -20,8 +20,8 @@ public class ApiRunner extends Assertions { .orElse(DEFAULT_KEY); final RequestQueueManager queueManager = (DEFAULT_KEY.equals(API_KEY)) - ? RequestQueueManager.ANONYMOUS - : RequestQueueManager.FREE_PLAN; + ? RequestQueueManager.anonymous() + : RequestQueueManager.planFree(); API = EtherScanAPI.builder() .withApiKey(ApiRunner.API_KEY) @@ -30,10 +30,6 @@ public class ApiRunner extends Assertions { .build(); } - public static String getApiKey() { - return API_KEY; - } - public static EtherScanAPI getApi() { return API; } diff --git a/src/test/java/io/goodforgod/api/etherscan/EtherScanAPITests.java b/src/test/java/io/goodforgod/api/etherscan/EtherScanAPITests.java index c50b03a..36e23ec 100644 --- a/src/test/java/io/goodforgod/api/etherscan/EtherScanAPITests.java +++ b/src/test/java/io/goodforgod/api/etherscan/EtherScanAPITests.java @@ -68,7 +68,6 @@ void timeout() throws InterruptedException { TimeUnit.SECONDS.sleep(5); Supplier supplier = () -> new UrlEthHttpClient(Duration.ofMillis(300), Duration.ofMillis(300)); EtherScanAPI api = EtherScanAPI.builder() - .withApiKey(getApiKey()) .withNetwork(() -> URI.create("https://api-unknown.etherscan.io/api")) .withHttpClient(supplier) .build(); diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxErc20Tests.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxErc20Tests.java index 928b2e3..b26bcee 100644 --- a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxErc20Tests.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxErc20Tests.java @@ -18,7 +18,7 @@ void correct() { assertNotNull(txs); assertEquals(3, txs.size()); assertTxs(txs); - assertNotEquals(0, txs.get(0).getGasPrice()); + assertNotEquals(0, txs.get(0).getGasPrice().asWei().intValue()); assertNotEquals(-1, txs.get(0).getNonce()); assertNotNull(txs.get(0).toString()); @@ -71,7 +71,7 @@ private void assertTxs(List txs) { assertNotNull(tx.getTokenDecimal()); assertNotEquals(-1, (tx.getConfirmations())); assertNotNull(tx.getGasUsed()); - assertNotEquals(-1, tx.getCumulativeGasUsed()); + assertNotEquals(-1, tx.getGasUsedCumulative()); assertNotEquals(-1, tx.getTransactionIndex()); } } diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc1155TokenTests.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc1155TokenTests.java index 0430dc8..f8cae43 100644 --- a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc1155TokenTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc1155TokenTests.java @@ -75,7 +75,7 @@ private void asserTx(TxErc1155 tx) { assertNotNull(tx.getTokenValue()); assertNotEquals(-1, (tx.getConfirmations())); assertNotNull(tx.getGasUsed()); - assertNotEquals(-1, tx.getCumulativeGasUsed()); + assertNotEquals(-1, tx.getGasUsedCumulative()); assertNotEquals(-1, tx.getTransactionIndex()); } } diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTests.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTests.java index 9a5a322..ca86256 100644 --- a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTests.java @@ -73,7 +73,7 @@ private void assertTxs(List txs) { assertNotNull(tx.getTokenDecimal()); assertNotEquals(-1, (tx.getConfirmations())); assertNotNull(tx.getGasUsed()); - assertNotEquals(-1, tx.getCumulativeGasUsed()); + assertNotEquals(-1, tx.getGasUsedCumulative()); assertNotEquals(-1, tx.getTransactionIndex()); } } diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxsTests.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxsTests.java index 3ee8ad1..653f62a 100644 --- a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxsTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxsTests.java @@ -24,7 +24,7 @@ void correct() { assertNotNull(txs.get(0).getTo()); assertNotNull(txs.get(0).getBlockHash()); assertNotNull(txs.get(0).getGas()); - assertNotNull(txs.get(0).getCumulativeGasUsed()); + assertNotNull(txs.get(0).getGasUsedCumulative()); assertNotNull(txs.get(0).getGasPrice()); assertNotNull(txs.get(0).getValue()); assertNotNull(txs.get(0).getContractAddress()); @@ -75,7 +75,7 @@ private void assertTxs(List txs) { assertNotEquals(-1, (tx.getNonce())); assertNotEquals(0, (tx.getTransactionIndex())); assertNotEquals(0, tx.getConfirmations()); - assertNotNull(tx.getTxreceipt_status()); + assertNotNull(tx.getTxReceiptStatus()); } } } diff --git a/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java b/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java index 9a7e426..806865d 100644 --- a/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java @@ -132,12 +132,12 @@ void txBuilder() { .withBlockNumber(1L) .withConfirmations(1L) .withContractAddress("1") - .withCumulativeGasUsed(BigInteger.ONE) .withFrom("1") .withTo("1") - .withGas(BigInteger.ONE) - .withGasPrice(BigInteger.ONE) - .withGasUsed(BigInteger.ONE) + .withCumulativeGasUsed(Wei.ofWei(BigInteger.ONE)) + .withGas(Wei.ofWei(BigInteger.ONE)) + .withGasPrice(Wei.ofWei(BigInteger.ONE)) + .withGasUsed(Wei.ofWei(BigInteger.ONE)) .withHash("1") .withInput("1") .withIsError("1") @@ -145,7 +145,7 @@ void txBuilder() { .withTimeStamp(timestamp) .withValue(BigInteger.ONE) .withTransactionIndex(1) - .withTxreceiptStatus("1") + .withTxReceiptStatus("1") .build(); assertNotNull(value); @@ -162,12 +162,12 @@ void txErc20Builder() { .withBlockNumber(1L) .withConfirmations(1L) .withContractAddress("1") - .withCumulativeGasUsed(BigInteger.ONE) .withFrom("1") .withTo("1") - .withGas(BigInteger.ONE) - .withGasPrice(BigInteger.ONE) - .withGasUsed(BigInteger.ONE) + .withCumulativeGasUsed(Wei.ofWei(BigInteger.ONE)) + .withGas(Wei.ofWei(BigInteger.ONE)) + .withGasPrice(Wei.ofWei(BigInteger.ONE)) + .withGasUsed(Wei.ofWei(BigInteger.ONE)) .withHash("1") .withInput("1") .withTokenName("1") @@ -192,12 +192,12 @@ void txErc721Builder() { .withBlockNumber(1L) .withConfirmations(1L) .withContractAddress("1") - .withCumulativeGasUsed(BigInteger.ONE) .withFrom("1") .withTo("1") - .withGas(BigInteger.ONE) - .withGasPrice(BigInteger.ONE) - .withGasUsed(BigInteger.ONE) + .withCumulativeGasUsed(Wei.ofWei(BigInteger.ONE)) + .withGas(Wei.ofWei(BigInteger.ONE)) + .withGasPrice(Wei.ofWei(BigInteger.ONE)) + .withGasUsed(Wei.ofWei(BigInteger.ONE)) .withHash("1") .withInput("1") .withTokenName("1") @@ -222,12 +222,12 @@ void txErc1155Builder() { .withBlockNumber(1L) .withConfirmations(1L) .withContractAddress("1") - .withCumulativeGasUsed(BigInteger.ONE) .withFrom("1") .withTo("1") - .withGas(BigInteger.ONE) - .withGasPrice(BigInteger.ONE) - .withGasUsed(BigInteger.ONE) + .withCumulativeGasUsed(Wei.ofWei(BigInteger.ONE)) + .withGas(Wei.ofWei(BigInteger.ONE)) + .withGasPrice(Wei.ofWei(BigInteger.ONE)) + .withGasUsed(Wei.ofWei(BigInteger.ONE)) .withHash("1") .withInput("1") .withTokenName("1") @@ -253,8 +253,8 @@ void txInternalBuilder() { .withFrom("1") .withTo("1") .withValue(BigInteger.ONE) - .withGas(BigInteger.ONE) - .withGasUsed(BigInteger.ONE) + .withGas(Wei.ofWei(BigInteger.ONE)) + .withGasUsed(Wei.ofWei(BigInteger.ONE)) .withHash("1") .withInput("1") .withTimeStamp(timestamp) diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTests.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTests.java index 874ccc0..363d5a2 100644 --- a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyBlockApiTests.java @@ -1,9 +1,6 @@ package io.goodforgod.api.etherscan.proxy; import io.goodforgod.api.etherscan.ApiRunner; -import io.goodforgod.api.etherscan.EthNetworks; -import io.goodforgod.api.etherscan.EtherScanAPI; -import io.goodforgod.api.etherscan.manager.RequestQueueManager; import io.goodforgod.api.etherscan.model.proxy.BlockProxy; import java.util.Optional; import org.junit.jupiter.api.Test; @@ -14,17 +11,9 @@ */ class ProxyBlockApiTests extends ApiRunner { - private final EtherScanAPI api; - - ProxyBlockApiTests() { - final RequestQueueManager queueManager = RequestQueueManager.ANONYMOUS; - this.api = EtherScanAPI.builder().withApiKey(getApiKey()).withNetwork(EthNetworks.MAINNET).withQueue(queueManager) - .build(); - } - @Test void correct() { - Optional block = api.proxy().block(5120); + Optional block = getApi().proxy().block(5120); assertTrue(block.isPresent()); BlockProxy proxy = block.get(); assertNotNull(proxy.getHash()); @@ -57,13 +46,13 @@ void correct() { @Test void correctParamWithEmptyExpectedResult() { - Optional block = api.proxy().block(99999999999L); + Optional block = getApi().proxy().block(99999999999L); assertFalse(block.isPresent()); } @Test void correctParamNegativeNo() { - Optional block = api.proxy().block(-1); + Optional block = getApi().proxy().block(-1); assertTrue(block.isPresent()); assertNotNull(block.get().getHash()); } From 14ccb53db6d1b6e28a273dd1dbb79ba5a8123986 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Mon, 15 May 2023 00:19:41 +0300 Subject: [PATCH 40/61] [2.0.0-SNAPSHOT] Test asserts fixed Log contract improved Tests reinforced --- .../api/etherscan/model/EthSupply.java | 26 ++++- .../goodforgod/api/etherscan/model/Log.java | 23 ++-- .../account/AccountTxErc20Tests.java | 2 +- .../account/AccountTxRc1155TokenTests.java | 4 +- .../account/AccountTxRc721TokenTests.java | 4 +- .../etherscan/model/ModelBuilderTests.java | 104 +++++++++++++++++- .../StatisticTokenSupplyApiTests.java | 2 +- 7 files changed, 138 insertions(+), 27 deletions(-) diff --git a/src/main/java/io/goodforgod/api/etherscan/model/EthSupply.java b/src/main/java/io/goodforgod/api/etherscan/model/EthSupply.java index f967360..344e754 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/EthSupply.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/EthSupply.java @@ -4,8 +4,6 @@ import java.util.Objects; /** - * Please Add Description Here. - * * @author Anton Kurako (GoodforGod) * @since 14.05.2023 */ @@ -100,10 +98,26 @@ public EthSupplyBuilder withWithdrawnTotal(Wei withdrawnTotal) { public EthSupply build() { EthSupply ethSupply = new EthSupply(); - ethSupply.BurntFees = this.burntFees.toString(); - ethSupply.Eth2Staking = this.eth2Staking.toString(); - ethSupply.EthSupply = this.ethSupply.toString(); - ethSupply.WithdrawnTotal = this.withdrawnTotal.toString(); + if (this.burntFees != null) { + ethSupply.BurntFees = this.burntFees.toString(); + } else { + ethSupply.BurntFees = BigInteger.ZERO.toString(); + } + if (this.eth2Staking != null) { + ethSupply.Eth2Staking = this.eth2Staking.toString(); + } else { + ethSupply.Eth2Staking = BigInteger.ZERO.toString(); + } + if (this.ethSupply != null) { + ethSupply.EthSupply = this.ethSupply.toString(); + } else { + ethSupply.EthSupply = BigInteger.ZERO.toString(); + } + if (this.withdrawnTotal != null) { + ethSupply.WithdrawnTotal = this.withdrawnTotal.toString(); + } else { + ethSupply.WithdrawnTotal = BigInteger.ZERO.toString(); + } return ethSupply; } } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Log.java b/src/main/java/io/goodforgod/api/etherscan/model/Log.java index 07e652f..8e14b16 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Log.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Log.java @@ -2,7 +2,6 @@ import com.google.gson.annotations.Expose; import io.goodforgod.api.etherscan.util.BasicUtils; -import java.math.BigInteger; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.List; @@ -28,10 +27,10 @@ public class Log { private String data; private String gasPrice; @Expose(deserialize = false, serialize = false) - private BigInteger _gasPrice; + private Wei _gasPrice; private String gasUsed; @Expose(deserialize = false, serialize = false) - private BigInteger _gasUsed; + private Wei _gasUsed; private List topics; private String logIndex; @Expose(deserialize = false, serialize = false) @@ -93,17 +92,17 @@ public String getData() { return data; } - public BigInteger getGasPrice() { + public Wei getGasPrice() { if (!BasicUtils.isEmpty(gasPrice)) { - _gasPrice = BasicUtils.parseHex(gasPrice); + _gasPrice = Wei.ofWei(BasicUtils.parseHex(gasPrice)); } return _gasPrice; } - public BigInteger getGasUsed() { + public Wei getGasUsed() { if (!BasicUtils.isEmpty(gasUsed)) { - _gasUsed = BasicUtils.parseHex(gasUsed); + _gasUsed = Wei.ofWei(BasicUtils.parseHex(gasUsed)); } return _gasUsed; @@ -172,8 +171,8 @@ public static final class LogBuilder { private Long transactionIndex; private LocalDateTime timeStamp; private String data; - private BigInteger gasPrice; - private BigInteger gasUsed; + private Wei gasPrice; + private Wei gasUsed; private List topics; private Long logIndex; @@ -209,12 +208,12 @@ public LogBuilder withData(String data) { return this; } - public LogBuilder withGasPrice(BigInteger gasPrice) { + public LogBuilder withGasPrice(Wei gasPrice) { this.gasPrice = gasPrice; return this; } - public LogBuilder withGasUsed(BigInteger gasUsed) { + public LogBuilder withGasUsed(Wei gasUsed) { this.gasUsed = gasUsed; return this; } @@ -233,7 +232,6 @@ public Log build() { Log log = new Log(); log.address = this.address; if (this.gasPrice != null) { - log.gasPrice = String.valueOf(this.gasPrice); log._gasPrice = this.gasPrice; } log._logIndex = this.logIndex; @@ -246,7 +244,6 @@ public Log build() { } log.data = this.data; if (this.gasUsed != null) { - log.gasUsed = String.valueOf(this.gasUsed); log._gasUsed = this.gasUsed; } log.logIndex = String.valueOf(this.logIndex); diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxErc20Tests.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxErc20Tests.java index b26bcee..4239bcd 100644 --- a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxErc20Tests.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxErc20Tests.java @@ -71,7 +71,7 @@ private void assertTxs(List txs) { assertNotNull(tx.getTokenDecimal()); assertNotEquals(-1, (tx.getConfirmations())); assertNotNull(tx.getGasUsed()); - assertNotEquals(-1, tx.getGasUsedCumulative()); + assertNotEquals(-1, tx.getGasUsedCumulative().asWei().intValue()); assertNotEquals(-1, tx.getTransactionIndex()); } } diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc1155TokenTests.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc1155TokenTests.java index f8cae43..d8cbb73 100644 --- a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc1155TokenTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc1155TokenTests.java @@ -18,7 +18,7 @@ void correct() { assertNotNull(txs); assertFalse(txs.isEmpty()); assertTxs(txs); - assertNotEquals(0, txs.get(0).getGasPrice()); + assertNotEquals(0, txs.get(0).getGasPrice().asWei().intValue()); assertNotEquals(-1, txs.get(0).getNonce()); assertNotNull(txs.get(0).toString()); @@ -75,7 +75,7 @@ private void asserTx(TxErc1155 tx) { assertNotNull(tx.getTokenValue()); assertNotEquals(-1, (tx.getConfirmations())); assertNotNull(tx.getGasUsed()); - assertNotEquals(-1, tx.getGasUsedCumulative()); + assertNotEquals(-1, tx.getGasUsedCumulative().asWei().intValue()); assertNotEquals(-1, tx.getTransactionIndex()); } } diff --git a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTests.java b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTests.java index ca86256..6c61a4c 100644 --- a/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/account/AccountTxRc721TokenTests.java @@ -18,7 +18,7 @@ void correct() { assertNotNull(txs); assertEquals(16, txs.size()); assertTxs(txs); - assertNotEquals(0, txs.get(0).getGasPrice()); + assertNotEquals(0, txs.get(0).getGasPrice().asWei().intValue()); assertNotEquals(-1, txs.get(0).getNonce()); assertNotNull(txs.get(0).toString()); @@ -73,7 +73,7 @@ private void assertTxs(List txs) { assertNotNull(tx.getTokenDecimal()); assertNotEquals(-1, (tx.getConfirmations())); assertNotNull(tx.getGasUsed()); - assertNotEquals(-1, tx.getGasUsedCumulative()); + assertNotEquals(-1, tx.getGasUsedCumulative().asWei().intValue()); assertNotEquals(-1, tx.getTransactionIndex()); } } diff --git a/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java b/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java index 806865d..9018ec8 100644 --- a/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java @@ -1,8 +1,12 @@ package io.goodforgod.api.etherscan.model; +import io.goodforgod.api.etherscan.model.proxy.BlockProxy; +import io.goodforgod.api.etherscan.model.proxy.ReceiptProxy; +import io.goodforgod.api.etherscan.model.proxy.TxProxy; import java.math.BigDecimal; import java.math.BigInteger; import java.time.LocalDateTime; +import java.util.Arrays; import java.util.Collections; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -84,8 +88,8 @@ void logBuilder() { .withAddress("1") .withBlockNumber(1L) .withData("1") - .withGasPrice(BigInteger.ONE) - .withGasUsed(BigInteger.ONE) + .withGasPrice(Wei.ofWei(1)) + .withGasUsed(Wei.ofWei(1)) .withLogIndex(1L) .withTimeStamp(timestamp) .withTransactionHash("1") @@ -268,4 +272,100 @@ void txInternalBuilder() { assertEquals("1", value.getTo()); assertEquals("1", value.getFrom()); } + + @Test + void ethSupplyBuilder() { + EthSupply value = EthSupply.builder() + .withBurntFees(Wei.ofWei(1)) + .withEth2Staking(Wei.ofWei(1)) + .withEthSupply(Wei.ofWei(1)) + .withWithdrawnTotal(Wei.ofWei(1)) + .build(); + + assertNotNull(value); + assertEquals(BigInteger.valueOf(1), value.getTotal().asWei()); + + EthSupply valueEmpty = EthSupply.builder() + .build(); + assertNotNull(valueEmpty); + assertEquals(BigInteger.ZERO, valueEmpty.getTotal().asWei()); + } + + @Test + void receiptProxyBuilder() { + LocalDateTime timestamp = LocalDateTime.now(); + ReceiptProxy value = ReceiptProxy.builder() + .withBlockHash("1") + .withBlockNumber(1L) + .withContractAddress("1") + .withCumulativeGasUsed(Wei.ofWei(1)) + .withFrom("1") + .withTo("1") + .withGasUsed(Wei.ofWei(1)) + .withRoot("1") + .withLogsBloom("1") + .withTransactionHash("1") + .withTransactionIndex(1L) + .withLogs(Arrays.asList(Log.builder() + .withTopics(Arrays.asList("1")) + .withTransactionIndex(1L) + .withTransactionHash("1") + .withTimeStamp(timestamp) + .withLogIndex(1L) + .withGasUsed(Wei.ofWei(1)) + .withGasPrice(Wei.ofWei(1)) + .withData("1") + .withAddress("1") + .build())) + .build(); + + assertNotNull(value); + assertEquals(BigInteger.valueOf(1), value.getGasUsed().asWei()); + } + + @Test + void blockProxyBuilder() { + LocalDateTime timestamp = LocalDateTime.now(); + BlockProxy value = BlockProxy.builder() + .withGasUsed(Wei.ofWei(1)) + .withLogsBloom("1") + .withDifficulty("1") + .withExtraData("1") + .withGasLimit(Wei.ofWei(1)) + .withHash("1") + .withMiner("1") + .withMixHash("1") + .withNonce("1") + .withNumber(1L) + .withParentHash("1") + .withReceiptsRoot("1") + .withSha3Uncles("1") + .withSize(1L) + .withStateRoot("1") + .withTimestamp(timestamp) + .withTotalDifficulty("1") + .withTransactionsRoot("1") + .withUncles(Arrays.asList("1")) + .withTransactions(Arrays.asList(TxProxy.builder() + .withBlockHash("1") + .withBlockNumber(1L) + .withFrom("1") + .withGas(Wei.ofWei(1)) + .withGasPrice(Wei.ofWei(1)) + .withHash("1") + .withInput("1") + .withNonce(1L) + .withR("1") + .withS("1") + .withTo("1") + .withTransactionIndex(1L) + .withV("1") + .withValue("1") + .withV("1") + .build())) + .build(); + + assertNotNull(value); + assertEquals(BigInteger.valueOf(1), value.getGasUsed().asWei()); + } } diff --git a/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticTokenSupplyApiTests.java b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticTokenSupplyApiTests.java index b21b3b3..6eff846 100644 --- a/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticTokenSupplyApiTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticTokenSupplyApiTests.java @@ -16,7 +16,7 @@ class StatisticTokenSupplyApiTests extends ApiRunner { void correct() { Wei supply = getApi().stats().supply("0x57d90b64a1a57749b0f932f1a3395792e12e7055"); assertNotNull(supply); - assertNotEquals(BigInteger.ZERO, supply); + assertNotEquals(BigInteger.ZERO, supply.asWei()); } @Test From bf30d9a2df7567a86ae494cbbb7ba62b49a2dfcb Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Mon, 15 May 2023 00:27:58 +0300 Subject: [PATCH 41/61] [2.0.0-SNAPSHOT] 1005L->1015L --- .../api/etherscan/manager/RequestQueueManager.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java b/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java index 449daca..0f36b23 100644 --- a/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java +++ b/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java @@ -17,7 +17,7 @@ public interface RequestQueueManager extends AutoCloseable { * Is used by default when no API KEY is provided */ static RequestQueueManager anonymous() { - return new SemaphoreRequestQueueManager(1, Duration.ofMillis(5005L)); + return new SemaphoreRequestQueueManager(1, Duration.ofMillis(5015L)); } /** @@ -25,19 +25,19 @@ static RequestQueueManager anonymous() { * Free API KEY */ static RequestQueueManager planFree() { - return new SemaphoreRequestQueueManager(5, Duration.ofMillis(1005L)); + return new SemaphoreRequestQueueManager(5, Duration.ofMillis(1015L)); } static RequestQueueManager planStandard() { - return new SemaphoreRequestQueueManager(10, Duration.ofMillis(1005L)); + return new SemaphoreRequestQueueManager(10, Duration.ofMillis(1015L)); } static RequestQueueManager planAdvanced() { - return new SemaphoreRequestQueueManager(20, Duration.ofMillis(1005L)); + return new SemaphoreRequestQueueManager(20, Duration.ofMillis(1015L)); } static RequestQueueManager planProfessional() { - return new SemaphoreRequestQueueManager(30, Duration.ofMillis(1005L)); + return new SemaphoreRequestQueueManager(30, Duration.ofMillis(1015L)); } static RequestQueueManager unlimited() { From 3a3e409ac48b4d039e9ef2077e98e12ca189293d Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Mon, 15 May 2023 00:41:05 +0300 Subject: [PATCH 42/61] [2.0.0-SNAPSHOT] Tests reinforced --- .../api/etherscan/model/GasEstimate.java | 4 +-- .../etherscan/model/ModelBuilderTests.java | 31 +++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/goodforgod/api/etherscan/model/GasEstimate.java b/src/main/java/io/goodforgod/api/etherscan/model/GasEstimate.java index 7f1e61d..198e53c 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/GasEstimate.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/GasEstimate.java @@ -40,8 +40,6 @@ public int hashCode() { @Override public String toString() { - return "GasEstimate{" + - "duration=" + duration + - '}'; + return duration.toString(); } } diff --git a/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java b/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java index 9018ec8..464b379 100644 --- a/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java @@ -79,6 +79,18 @@ void gasOracleBuilder() { assertNotNull(value); assertEquals(Wei.ofWei(1000000000), value.getFastGasPriceInWei()); + + GasOracle value2 = GasOracle.builder() + .withFastGasPrice(Wei.ofWei(1000000000)) + .withProposeGasPrice(Wei.ofWei(1000000000)) + .withSafeGasPrice(Wei.ofWei(1000000000)) + .withGasUsedRatio(Collections.singletonList(new BigDecimal(1))) + .withLastBlock(1L) + .withSuggestBaseFee(1.0) + .build(); + assertEquals(value, value2); + assertEquals(value.hashCode(), value2.hashCode()); + assertEquals(value.toString(), value2.toString()); } @Test @@ -289,6 +301,16 @@ void ethSupplyBuilder() { .build(); assertNotNull(valueEmpty); assertEquals(BigInteger.ZERO, valueEmpty.getTotal().asWei()); + + EthSupply value2 = EthSupply.builder() + .withBurntFees(Wei.ofWei(1)) + .withEth2Staking(Wei.ofWei(1)) + .withEthSupply(Wei.ofWei(1)) + .withWithdrawnTotal(Wei.ofWei(1)) + .build(); + assertEquals(value, value2); + assertEquals(value.hashCode(), value2.hashCode()); + assertEquals(value.toString(), value2.toString()); } @Test @@ -368,4 +390,13 @@ void blockProxyBuilder() { assertNotNull(value); assertEquals(BigInteger.valueOf(1), value.getGasUsed().asWei()); } + + @Test + void gasEstimate() { + GasEstimate gas1 = new GasEstimate(1); + GasEstimate gas2 = new GasEstimate(1); + assertEquals(gas1, gas2); + assertEquals(gas1.hashCode(), gas2.hashCode()); + assertEquals(gas1.toString(), gas2.toString()); + } } From 2d666bcdc67a838483fda2bc81b1f98c9601f355 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Mon, 15 May 2023 00:57:16 +0300 Subject: [PATCH 43/61] [2.0.0-SNAPSHOT] Log simplified Tests reinforced --- .../goodforgod/api/etherscan/model/Log.java | 12 ++-- .../etherscan/model/ModelBuilderTests.java | 72 +++++++++++++++++++ 2 files changed, 77 insertions(+), 7 deletions(-) diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Log.java b/src/main/java/io/goodforgod/api/etherscan/model/Log.java index 8e14b16..2808462 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Log.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Log.java @@ -64,28 +64,26 @@ public Long getTransactionIndex() { public LocalDateTime getTimeStamp() { if (_timeStamp == null && !BasicUtils.isEmpty(timeStamp)) { - long formatted = (timeStamp.charAt(0) == '0' && timeStamp.charAt(1) == 'x') - ? BasicUtils.parseHex(timeStamp).longValue() - : Long.parseLong(timeStamp); + long formatted = getTimeStampAsSeconds(); _timeStamp = LocalDateTime.ofEpochSecond(formatted, 0, ZoneOffset.UTC); } return _timeStamp; } /** - * Return the "timeStamp" field of the event record as a long-int representing the milliseconds + * Return the "timeStamp" field of the event record as a long-int representing the seconds * since the Unix epoch (1970-01-01 00:00:00). * * @return milliseconds between Unix epoch and `timeStamp`. If field is empty or null, returns null */ - public Long getTimeStampAsMillis() { + public Long getTimeStampAsSeconds() { if (BasicUtils.isEmpty(timeStamp)) { return null; } - long tsSecs = (timeStamp.charAt(0) == '0' && timeStamp.charAt(1) == 'x') + + return (timeStamp.charAt(0) == '0' && timeStamp.charAt(1) == 'x') ? BasicUtils.parseHex(timeStamp).longValue() : Long.parseLong(timeStamp); - return tsSecs * 1000; } public String getData() { diff --git a/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java b/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java index 464b379..f895595 100644 --- a/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java @@ -168,6 +168,31 @@ void txBuilder() { assertTrue(value.haveError()); assertEquals("1", value.getTo()); assertEquals("1", value.getFrom()); + + Tx value2 = Tx.builder() + .withBlockHash("1") + .withBlockNumber(1L) + .withConfirmations(1L) + .withContractAddress("1") + .withFrom("1") + .withTo("1") + .withCumulativeGasUsed(Wei.ofWei(BigInteger.ONE)) + .withGas(Wei.ofWei(BigInteger.ONE)) + .withGasPrice(Wei.ofWei(BigInteger.ONE)) + .withGasUsed(Wei.ofWei(BigInteger.ONE)) + .withHash("1") + .withInput("1") + .withIsError("1") + .withNonce(1L) + .withTimeStamp(timestamp) + .withValue(BigInteger.ONE) + .withTransactionIndex(1) + .withTxReceiptStatus("1") + .build(); + + assertEquals(value, value2); + assertEquals(value.hashCode(), value2.hashCode()); + assertEquals(value.toString(), value2.toString()); } @Test @@ -258,6 +283,32 @@ void txErc1155Builder() { assertNotNull(value); assertEquals("1", value.getTo()); assertEquals("1", value.getFrom()); + + TxErc1155 value2 = TxErc1155.builder() + .withBlockHash("1") + .withBlockNumber(1L) + .withConfirmations(1L) + .withContractAddress("1") + .withFrom("1") + .withTo("1") + .withCumulativeGasUsed(Wei.ofWei(BigInteger.ONE)) + .withGas(Wei.ofWei(BigInteger.ONE)) + .withGasPrice(Wei.ofWei(BigInteger.ONE)) + .withGasUsed(Wei.ofWei(BigInteger.ONE)) + .withHash("1") + .withInput("1") + .withTokenName("1") + .withTokenSymbol("1") + .withTokenDecimal("1") + .withTokenID("1") + .withNonce(1L) + .withTimeStamp(timestamp) + .withTransactionIndex(1) + .build(); + + assertEquals(value, value2); + assertEquals(value.hashCode(), value2.hashCode()); + assertEquals(value.toString(), value2.toString()); } @Test @@ -283,6 +334,27 @@ void txInternalBuilder() { assertNotNull(value); assertEquals("1", value.getTo()); assertEquals("1", value.getFrom()); + + TxInternal value2 = TxInternal.builder() + .withBlockNumber(1L) + .withContractAddress("1") + .withFrom("1") + .withTo("1") + .withValue(BigInteger.ONE) + .withGas(Wei.ofWei(BigInteger.ONE)) + .withGasUsed(Wei.ofWei(BigInteger.ONE)) + .withHash("1") + .withInput("1") + .withTimeStamp(timestamp) + .withErrCode("1") + .withIsError(1) + .withTraceId("1") + .withType("1") + .build(); + + assertEquals(value, value2); + assertEquals(value.hashCode(), value2.hashCode()); + assertEquals(value.toString(), value2.toString()); } @Test From f09f38a1403a9849ddbb1628a8de61b52620618a Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Mon, 15 May 2023 01:01:44 +0300 Subject: [PATCH 44/61] [2.0.0] Release prepared --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index e809e6c..821da06 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ groupId=com.github.goodforgod artifactId=java-etherscan-api -artifactVersion=2.0.0-SNAPSHOT +artifactVersion=2.0.0 ##### GRADLE ##### From d1ec9e52ccb84be8bc42805dba075549c9b158d6 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Mon, 15 May 2023 01:04:11 +0300 Subject: [PATCH 45/61] [2.0.0] Release prepared --- README.md | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 4cff68d..dc939bf 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,6 @@ [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=GoodforGod_java-etherscan-api&metric=coverage)](https://sonarcloud.io/dashboard?id=GoodforGod_java-etherscan-api) [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=GoodforGod_java-etherscan-api&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=GoodforGod_java-etherscan-api) [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=GoodforGod_java-etherscan-api&metric=ncloc)](https://sonarcloud.io/dashboard?id=GoodforGod_java-etherscan-api) -[![](https://jitpack.io/v/GoodforGod/java-etherscan-api.svg)](https://jitpack.io/#GoodforGod/java-etherscan-api) [Etherscan.io](https://etherscan.io/apis) Java API implementation. @@ -15,7 +14,7 @@ Library supports all available EtherScan *API* calls for all available *Ethereum **Gradle** ```groovy -implementation "com.github.goodforgod:java-etherscan-api:2.0.0-SNAPSHOT" +implementation "com.github.goodforgod:java-etherscan-api:2.0.0" ``` **Maven** @@ -23,7 +22,7 @@ implementation "com.github.goodforgod:java-etherscan-api:2.0.0-SNAPSHOT" com.github.goodforgod java-etherscan-api - 2.0.0-SNAPSHOT + 2.0.0 ``` @@ -41,9 +40,9 @@ implementation "com.github.goodforgod:java-etherscan-api:2.0.0-SNAPSHOT" - [Token](#token-api) - [Version History](#version-history) -## Mainnet and Testnets +## MainNet and TestNets -API support Ethereum [default networks](https://docs.etherscan.io/getting-started/endpoint-urls): +API support all Ethereum [default networks](https://docs.etherscan.io/getting-started/endpoint-urls): - [Mainnet](https://api.etherscan.io/) - [Goerli](https://api-goerli.etherscan.io/) - [Sepolia](https://api-sepolia.etherscan.io/) @@ -121,8 +120,8 @@ Abi abi = api.contract().contractAbi("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413 **Get event logs for single topic** ```java EtherScanAPI api = EtherScanAPI.build(); -LogQuery query = LogQueryBuilder.with("0x33990122638b9132ca29c723bdf037f1a891a70c") - .topic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") +LogQuery query = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") + .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") .build(); List logs = api.logs().logs(query); ``` @@ -163,7 +162,7 @@ Optional block = api.proxy().block(15215); **Statistic about last price** ```java EtherScanAPI api = EtherScanAPI.build(); -Price price = api.stats().lastPrice(); +Price price = api.stats().priceLast(); ``` ### Transaction API From b05bd8adf89d56857d0769227b28f3584d8051ca Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Mon, 15 May 2023 20:49:06 +0300 Subject: [PATCH 46/61] [2.0.0] Tx refactored and simplified and common parts moved to BlockTx Comparable for multiple models added GasOracle simplified and reinforced Price reinforced GasEstimate.java removed as useless --- .../api/etherscan/GasTrackerAPI.java | 8 +- .../api/etherscan/GasTrackerAPIProvider.java | 6 +- .../api/etherscan/model/BaseTx.java | 17 +--- .../goodforgod/api/etherscan/model/Block.java | 9 +- .../api/etherscan/model/BlockTx.java | 79 +++++++++++++++++ .../api/etherscan/model/GasEstimate.java | 45 ---------- .../api/etherscan/model/GasOracle.java | 40 +++++---- .../goodforgod/api/etherscan/model/Log.java | 6 -- .../goodforgod/api/etherscan/model/Price.java | 19 ++-- .../api/etherscan/model/Status.java | 14 +-- .../api/etherscan/model/TokenBalance.java | 9 +- .../io/goodforgod/api/etherscan/model/Tx.java | 75 ++++------------ .../api/etherscan/model/TxErc1155.java | 54 ++++-------- .../api/etherscan/model/TxErc20.java | 53 ++++------- .../api/etherscan/model/TxErc721.java | 54 ++++-------- .../api/etherscan/model/TxInternal.java | 17 +++- .../goodforgod/api/etherscan/model/Wei.java | 87 +++++++++++++++---- .../api/etherscan/model/proxy/BlockProxy.java | 42 +++------ .../etherscan/model/proxy/ReceiptProxy.java | 36 ++------ .../api/etherscan/model/proxy/TxProxy.java | 46 ++++------ .../gastracker/GasTrackerApiTests.java | 5 +- .../etherscan/model/ModelBuilderTests.java | 60 ++++++++++--- .../proxy/ProxyTxReceiptApiTests.java | 2 +- 23 files changed, 368 insertions(+), 415 deletions(-) create mode 100644 src/main/java/io/goodforgod/api/etherscan/model/BlockTx.java delete mode 100644 src/main/java/io/goodforgod/api/etherscan/model/GasEstimate.java diff --git a/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPI.java b/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPI.java index 355d62a..6fce763 100644 --- a/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPI.java +++ b/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPI.java @@ -1,9 +1,9 @@ package io.goodforgod.api.etherscan; import io.goodforgod.api.etherscan.error.EtherScanException; -import io.goodforgod.api.etherscan.model.GasEstimate; import io.goodforgod.api.etherscan.model.GasOracle; import io.goodforgod.api.etherscan.model.Wei; +import java.time.Duration; import org.jetbrains.annotations.NotNull; /** @@ -16,13 +16,13 @@ public interface GasTrackerAPI { /** - * Returns the estimated time, in seconds, for a transaction to be confirmed on the blockchain. + * Returns the estimated time for a transaction to be confirmed on the blockchain. * - * @return fast, suggested gas price + * @return estimated time * @throws EtherScanException parent exception class */ @NotNull - GasEstimate estimate(@NotNull Wei wei) throws EtherScanException; + Duration estimate(@NotNull Wei wei) throws EtherScanException; /** * Returns the current Safe, Proposed and Fast gas prices. diff --git a/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java index 0b559d8..cbe0a75 100644 --- a/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java @@ -4,11 +4,11 @@ import io.goodforgod.api.etherscan.error.EtherScanResponseException; import io.goodforgod.api.etherscan.http.EthHttpClient; import io.goodforgod.api.etherscan.manager.RequestQueueManager; -import io.goodforgod.api.etherscan.model.GasEstimate; import io.goodforgod.api.etherscan.model.GasOracle; import io.goodforgod.api.etherscan.model.Wei; import io.goodforgod.api.etherscan.model.response.GasEstimateResponseTO; import io.goodforgod.api.etherscan.model.response.GasOracleResponseTO; +import java.time.Duration; import org.jetbrains.annotations.NotNull; /** @@ -33,13 +33,13 @@ final class GasTrackerAPIProvider extends BasicProvider implements GasTrackerAPI } @Override - public @NotNull GasEstimate estimate(@NotNull Wei wei) throws EtherScanException { + public @NotNull Duration estimate(@NotNull Wei wei) throws EtherScanException { final String urlParams = ACT_GAS_ESTIMATE_PARAM + GASPRICE_PARAM + wei.asWei().toString(); final GasEstimateResponseTO response = getRequest(urlParams, GasEstimateResponseTO.class); if (response.getStatus() != 1) throw new EtherScanResponseException(response); - return new GasEstimate(Long.parseLong(response.getResult())); + return Duration.ofSeconds(Long.parseLong(response.getResult())); } @NotNull diff --git a/src/main/java/io/goodforgod/api/etherscan/model/BaseTx.java b/src/main/java/io/goodforgod/api/etherscan/model/BaseTx.java index 64a9627..8de679a 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/BaseTx.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/BaseTx.java @@ -6,12 +6,13 @@ import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.Objects; +import org.jetbrains.annotations.NotNull; /** * @author GoodforGod * @since 28.10.2018 */ -abstract class BaseTx { +abstract class BaseTx implements Comparable { long blockNumber; String timeStamp; @@ -84,17 +85,7 @@ public int hashCode() { } @Override - public String toString() { - return "BaseTx{" + - "blockNumber=" + blockNumber + - ", timeStamp='" + timeStamp + '\'' + - ", hash='" + hash + '\'' + - ", from='" + from + '\'' + - ", to='" + to + '\'' + - ", contractAddress='" + contractAddress + '\'' + - ", input='" + input + '\'' + - ", gas=" + gas + - ", gasUsed=" + gasUsed + - '}'; + public int compareTo(@NotNull BaseTx o) { + return Long.compare(blockNumber, o.blockNumber); } } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Block.java b/src/main/java/io/goodforgod/api/etherscan/model/Block.java index 95bfbcb..9fe4e02 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Block.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Block.java @@ -6,12 +6,13 @@ import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.Objects; +import org.jetbrains.annotations.NotNull; /** * @author GoodforGod * @since 28.10.2018 */ -public class Block { +public class Block implements Comparable { long blockNumber; BigInteger blockReward; @@ -58,10 +59,14 @@ public String toString() { "blockNumber=" + blockNumber + ", blockReward=" + blockReward + ", timeStamp='" + timeStamp + '\'' + - ", _timeStamp=" + _timeStamp + '}'; } + @Override + public int compareTo(@NotNull Block o) { + return Long.compare(blockNumber, o.blockNumber); + } + public static BlockBuilder builder() { return new BlockBuilder(); } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/BlockTx.java b/src/main/java/io/goodforgod/api/etherscan/model/BlockTx.java new file mode 100644 index 0000000..f3c4d67 --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/BlockTx.java @@ -0,0 +1,79 @@ +package io.goodforgod.api.etherscan.model; + +import java.math.BigInteger; +import java.util.Objects; +import org.jetbrains.annotations.NotNull; + +/** + * @author Anton Kurako (GoodforGod) + * @since 15.05.2023 + */ +abstract class BlockTx extends BaseTx { + + long nonce; + String blockHash; + long transactionIndex; + long confirmations; + BigInteger gasPrice; + BigInteger cumulativeGasUsed; + + public long getNonce() { + return nonce; + } + + public String getBlockHash() { + return blockHash; + } + + public long getTransactionIndex() { + return transactionIndex; + } + + public Wei getGasPrice() { + return Wei.ofWei(gasPrice); + } + + public Wei getGasUsedCumulative() { + return Wei.ofWei(cumulativeGasUsed); + } + + public long getConfirmations() { + return confirmations; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (!(o instanceof BlockTx)) + return false; + if (!super.equals(o)) + return false; + BlockTx blockTx = (BlockTx) o; + return nonce == blockTx.nonce && transactionIndex == blockTx.transactionIndex + && Objects.equals(blockHash, blockTx.blockHash); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), nonce, blockHash, transactionIndex); + } + + @Override + public int compareTo(@NotNull BaseTx o) { + final int superCompare = super.compareTo(o); + if (superCompare == 0) { + if (o instanceof Tx) { + return Long.compare(transactionIndex, ((Tx) o).getTransactionIndex()); + } else if (o instanceof TxErc20) { + return Long.compare(transactionIndex, ((TxErc20) o).getTransactionIndex()); + } else if (o instanceof TxErc721) { + return Long.compare(transactionIndex, ((TxErc721) o).getTransactionIndex()); + } else if (o instanceof TxErc1155) { + return Long.compare(transactionIndex, ((TxErc1155) o).getTransactionIndex()); + } + } + + return superCompare; + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/GasEstimate.java b/src/main/java/io/goodforgod/api/etherscan/model/GasEstimate.java deleted file mode 100644 index 198e53c..0000000 --- a/src/main/java/io/goodforgod/api/etherscan/model/GasEstimate.java +++ /dev/null @@ -1,45 +0,0 @@ -package io.goodforgod.api.etherscan.model; - -import java.time.Duration; -import java.util.Objects; - -/** - * @author GoodforGod - * @since 14.05.2023 - */ -public class GasEstimate { - - private final Duration duration; - - public GasEstimate(long durationInSeconds) { - this.duration = Duration.ofSeconds(durationInSeconds); - } - - public GasEstimate(Duration duration) { - this.duration = duration; - } - - public Duration getDuration() { - return duration; - } - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (!(o instanceof GasEstimate)) - return false; - GasEstimate that = (GasEstimate) o; - return Objects.equals(duration, that.duration); - } - - @Override - public int hashCode() { - return Objects.hash(duration); - } - - @Override - public String toString() { - return duration.toString(); - } -} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java b/src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java index d273357..6fe1231 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/GasOracle.java @@ -14,10 +14,10 @@ public class GasOracle { private Long LastBlock; - private Integer SafeGasPrice; - private Integer ProposeGasPrice; - private Integer FastGasPrice; - private Double suggestBaseFee; + private BigInteger SafeGasPrice; + private BigInteger ProposeGasPrice; + private BigInteger FastGasPrice; + private BigDecimal suggestBaseFee; private String gasUsedRatio; protected GasOracle() {} @@ -27,18 +27,18 @@ public Long getLastBlock() { } public Wei getSafeGasPriceInWei() { - return Wei.ofWei(BigInteger.valueOf(SafeGasPrice).multiply(BigInteger.TEN.pow(9))); + return Wei.ofGwei(SafeGasPrice); } public Wei getProposeGasPriceInWei() { - return Wei.ofWei(BigInteger.valueOf(ProposeGasPrice).multiply(BigInteger.TEN.pow(9))); + return Wei.ofGwei(ProposeGasPrice); } public Wei getFastGasPriceInWei() { - return Wei.ofWei(BigInteger.valueOf(FastGasPrice).multiply(BigInteger.TEN.pow(9))); + return Wei.ofGwei(FastGasPrice); } - public Double getSuggestBaseFee() { + public BigDecimal getSuggestBaseFee() { return suggestBaseFee; } @@ -52,12 +52,14 @@ public List getGasUsedRatio() { public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) + if (!(o instanceof GasOracle)) return false; GasOracle gasOracle = (GasOracle) o; - return LastBlock.equals(gasOracle.LastBlock) && SafeGasPrice.equals(gasOracle.SafeGasPrice) - && ProposeGasPrice.equals(gasOracle.ProposeGasPrice) && FastGasPrice.equals(gasOracle.FastGasPrice) - && suggestBaseFee.equals(gasOracle.suggestBaseFee) && gasUsedRatio.equals(gasOracle.gasUsedRatio); + return Objects.equals(LastBlock, gasOracle.LastBlock) && Objects.equals(SafeGasPrice, gasOracle.SafeGasPrice) + && Objects.equals(ProposeGasPrice, gasOracle.ProposeGasPrice) + && Objects.equals(FastGasPrice, gasOracle.FastGasPrice) + && Objects.equals(suggestBaseFee, gasOracle.suggestBaseFee) + && Objects.equals(gasUsedRatio, gasOracle.gasUsedRatio); } @Override @@ -73,7 +75,7 @@ public String toString() { ", ProposeGasPrice=" + ProposeGasPrice + ", FastGasPrice=" + FastGasPrice + ", suggestBaseFee=" + suggestBaseFee + - ", gasUsedRatio='" + gasUsedRatio + '\'' + + ", gasUsedRatio=" + gasUsedRatio + '}'; } @@ -87,7 +89,7 @@ public static final class GasOracleBuilder { private Wei safeGasPrice; private Wei proposeGasPrice; private Wei fastGasPrice; - private Double suggestBaseFee; + private BigDecimal suggestBaseFee; private List gasUsedRatio; private GasOracleBuilder() {} @@ -112,7 +114,7 @@ public GasOracleBuilder withFastGasPrice(Wei fastGasPrice) { return this; } - public GasOracleBuilder withSuggestBaseFee(Double suggestBaseFee) { + public GasOracleBuilder withSuggestBaseFee(BigDecimal suggestBaseFee) { this.suggestBaseFee = suggestBaseFee; return this; } @@ -127,18 +129,18 @@ public GasOracle build() { gasOracle.LastBlock = this.lastBlock; gasOracle.suggestBaseFee = this.suggestBaseFee; if (this.proposeGasPrice != null) { - gasOracle.ProposeGasPrice = this.proposeGasPrice.asGwei().intValue(); + gasOracle.ProposeGasPrice = this.proposeGasPrice.asGwei().toBigInteger(); } if (this.safeGasPrice != null) { - gasOracle.SafeGasPrice = this.safeGasPrice.asGwei().intValue(); + gasOracle.SafeGasPrice = this.safeGasPrice.asGwei().toBigInteger(); } if (this.fastGasPrice != null) { - gasOracle.FastGasPrice = this.fastGasPrice.asGwei().intValue(); + gasOracle.FastGasPrice = this.fastGasPrice.asGwei().toBigInteger(); } if (this.gasUsedRatio != null) { gasOracle.gasUsedRatio = this.gasUsedRatio.stream() .map(BigDecimal::toString) - .collect(Collectors.joining(", ")); + .collect(Collectors.joining(",")); } return gasOracle; } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Log.java b/src/main/java/io/goodforgod/api/etherscan/model/Log.java index 2808462..da6c295 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Log.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Log.java @@ -139,21 +139,15 @@ public int hashCode() { public String toString() { return "Log{" + "blockNumber='" + blockNumber + '\'' + - ", _blockNumber=" + _blockNumber + ", address='" + address + '\'' + ", transactionHash='" + transactionHash + '\'' + ", transactionIndex='" + transactionIndex + '\'' + - ", _transactionIndex=" + _transactionIndex + ", timeStamp='" + timeStamp + '\'' + - ", _timeStamp=" + _timeStamp + ", data='" + data + '\'' + ", gasPrice='" + gasPrice + '\'' + - ", _gasPrice=" + _gasPrice + ", gasUsed='" + gasUsed + '\'' + - ", _gasUsed=" + _gasUsed + ", topics=" + topics + ", logIndex='" + logIndex + '\'' + - ", _logIndex=" + _logIndex + '}'; } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Price.java b/src/main/java/io/goodforgod/api/etherscan/model/Price.java index 4ef4491..d2a8091 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Price.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Price.java @@ -1,6 +1,7 @@ package io.goodforgod.api.etherscan.model; import com.google.gson.annotations.Expose; +import java.math.BigDecimal; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.Objects; @@ -11,8 +12,8 @@ */ public class Price { - private double ethusd; - private double ethbtc; + private BigDecimal ethusd; + private BigDecimal ethbtc; private String ethusd_timestamp; private String ethbtc_timestamp; @Expose(deserialize = false, serialize = false) @@ -22,11 +23,11 @@ public class Price { protected Price() {} - public double inUsd() { + public BigDecimal inUsd() { return ethusd; } - public double inBtc() { + public BigDecimal inBtc() { return ethbtc; } @@ -51,7 +52,7 @@ public boolean equals(Object o) { if (!(o instanceof Price)) return false; Price price = (Price) o; - return Double.compare(price.ethusd, ethusd) == 0 && Double.compare(price.ethbtc, ethbtc) == 0 + return Objects.equals(ethusd, price.ethusd) && Objects.equals(ethbtc, price.ethbtc) && Objects.equals(ethusd_timestamp, price.ethusd_timestamp) && Objects.equals(ethbtc_timestamp, price.ethbtc_timestamp); } @@ -77,19 +78,19 @@ public static PriceBuilder builder() { public static final class PriceBuilder { - private double ethusd; - private double ethbtc; + private BigDecimal ethusd; + private BigDecimal ethbtc; private LocalDateTime ethusdTimestamp; private LocalDateTime ethbtcTimestamp; private PriceBuilder() {} - public PriceBuilder withEthUsd(double ethusd) { + public PriceBuilder withEthUsd(BigDecimal ethusd) { this.ethusd = ethusd; return this; } - public PriceBuilder withEthBtc(double ethbtc) { + public PriceBuilder withEthBtc(BigDecimal ethbtc) { this.ethbtc = ethbtc; return this; } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Status.java b/src/main/java/io/goodforgod/api/etherscan/model/Status.java index eaf9b8a..052c187 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Status.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Status.java @@ -30,23 +30,15 @@ public String getErrDescription() { public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) + if (!(o instanceof Status)) return false; - Status status = (Status) o; - - if (isError != status.isError) - return false; - return Objects.equals(errDescription, status.errDescription); + return isError == status.isError && Objects.equals(errDescription, status.errDescription); } @Override public int hashCode() { - int result = isError; - result = 31 * result + (errDescription != null - ? errDescription.hashCode() - : 0); - return result; + return Objects.hash(isError, errDescription); } @Override diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TokenBalance.java b/src/main/java/io/goodforgod/api/etherscan/model/TokenBalance.java index 0c1a5b5..bb40ee2 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TokenBalance.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TokenBalance.java @@ -23,22 +23,17 @@ public String getContract() { public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) + if (!(o instanceof TokenBalance)) return false; if (!super.equals(o)) return false; - TokenBalance that = (TokenBalance) o; return Objects.equals(tokenContract, that.tokenContract); } @Override public int hashCode() { - int result = super.hashCode(); - result = 31 * result + (tokenContract != null - ? tokenContract.hashCode() - : 0); - return result; + return Objects.hash(super.hashCode(), tokenContract); } @Override diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Tx.java b/src/main/java/io/goodforgod/api/etherscan/model/Tx.java index 819252e..7ef0e22 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Tx.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Tx.java @@ -4,21 +4,14 @@ import java.math.BigInteger; import java.time.LocalDateTime; import java.time.ZoneOffset; -import java.util.Objects; /** * @author GoodforGod * @since 28.10.2018 */ -public class Tx extends BaseTx { +public class Tx extends BlockTx { private BigInteger value; - private long nonce; - private String blockHash; - private int transactionIndex; - private BigInteger gasPrice; - private BigInteger cumulativeGasUsed; - private long confirmations; private String isError; private String txreceipt_status; @@ -29,22 +22,6 @@ public BigInteger getValue() { return value; } - public long getNonce() { - return nonce; - } - - public String getBlockHash() { - return blockHash; - } - - public int getTransactionIndex() { - return transactionIndex; - } - - public Wei getGasPrice() { - return Wei.ofWei(gasPrice); - } - public boolean haveError() { return !BasicUtils.isEmpty(isError) && !isError.equals("0"); } @@ -52,50 +29,30 @@ public boolean haveError() { public String getTxReceiptStatus() { return txreceipt_status; } - - public Wei getGasUsedCumulative() { - return Wei.ofWei(cumulativeGasUsed); - } - - public long getConfirmations() { - return confirmations; - } // - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (!(o instanceof Tx)) - return false; - if (!super.equals(o)) - return false; - Tx tx = (Tx) o; - return nonce == tx.nonce && transactionIndex == tx.transactionIndex && confirmations == tx.confirmations - && Objects.equals(value, tx.value) && Objects.equals(blockHash, tx.blockHash) - && Objects.equals(gasPrice, tx.gasPrice) && Objects.equals(cumulativeGasUsed, tx.cumulativeGasUsed) - && Objects.equals(isError, tx.isError) && Objects.equals(txreceipt_status, tx.txreceipt_status); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), value, nonce, blockHash, transactionIndex, gasPrice, cumulativeGasUsed, - confirmations, isError, txreceipt_status); - } - @Override public String toString() { return "Tx{" + - "nonce=" + nonce + - ", value='" + value + '\'' + + "value=" + value + + ", isError='" + isError + '\'' + + ", txreceipt_status='" + txreceipt_status + '\'' + + ", nonce=" + nonce + ", blockHash='" + blockHash + '\'' + ", transactionIndex=" + transactionIndex + + ", confirmations=" + confirmations + ", gasPrice=" + gasPrice + ", cumulativeGasUsed=" + cumulativeGasUsed + - ", confirmations=" + confirmations + - ", isError='" + isError + '\'' + - ", txreceipt_status='" + txreceipt_status + '\'' + - "} " + super.toString(); + ", blockNumber=" + blockNumber + + ", timeStamp='" + timeStamp + '\'' + + ", hash='" + hash + '\'' + + ", from='" + from + '\'' + + ", to='" + to + '\'' + + ", contractAddress='" + contractAddress + '\'' + + ", input='" + input + '\'' + + ", gas=" + gas + + ", gasUsed=" + gasUsed + + '}'; } public static TxBuilder builder() { diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java b/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java index 7be8aff..16d4457 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java @@ -1,6 +1,5 @@ package io.goodforgod.api.etherscan.model; -import java.math.BigInteger; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.Objects; @@ -9,30 +8,16 @@ * @author GoodforGod * @since 28.10.2018 */ -public class TxErc1155 extends BaseTx { +public class TxErc1155 extends BlockTx { - private long nonce; - private String blockHash; private String tokenID; private String tokenName; private String tokenSymbol; private String tokenValue; - private int transactionIndex; - private BigInteger gasPrice; - private BigInteger cumulativeGasUsed; - private long confirmations; protected TxErc1155() {} // - public long getNonce() { - return nonce; - } - - public String getBlockHash() { - return blockHash; - } - public String getTokenID() { return tokenID; } @@ -48,22 +33,6 @@ public String getTokenSymbol() { public String getTokenValue() { return tokenValue; } - - public int getTransactionIndex() { - return transactionIndex; - } - - public Wei getGasPrice() { - return Wei.ofWei(gasPrice); - } - - public Wei getGasUsedCumulative() { - return Wei.ofWei(cumulativeGasUsed); - } - - public long getConfirmations() { - return confirmations; - } // @Override @@ -86,18 +55,27 @@ public int hashCode() { @Override public String toString() { - return "TxERC721{" + - "nonce=" + nonce + - ", blockHash='" + blockHash + '\'' + - ", tokenID='" + tokenID + '\'' + + return "TxErc1155{" + + "tokenID='" + tokenID + '\'' + ", tokenName='" + tokenName + '\'' + ", tokenSymbol='" + tokenSymbol + '\'' + ", tokenValue='" + tokenValue + '\'' + + ", nonce=" + nonce + + ", blockHash='" + blockHash + '\'' + ", transactionIndex=" + transactionIndex + + ", confirmations=" + confirmations + ", gasPrice=" + gasPrice + ", cumulativeGasUsed=" + cumulativeGasUsed + - ", confirmations=" + confirmations + - "} " + super.toString(); + ", blockNumber=" + blockNumber + + ", timeStamp='" + timeStamp + '\'' + + ", hash='" + hash + '\'' + + ", from='" + from + '\'' + + ", to='" + to + '\'' + + ", contractAddress='" + contractAddress + '\'' + + ", input='" + input + '\'' + + ", gas=" + gas + + ", gasUsed=" + gasUsed + + '}'; } public static TxErc1155Builder builder() { diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java b/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java index 751044c..3dc22fd 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java @@ -9,30 +9,16 @@ * @author GoodforGod * @since 28.10.2018 */ -public class TxErc20 extends BaseTx { +public class TxErc20 extends BlockTx { private BigInteger value; - private long nonce; - private String blockHash; private String tokenName; private String tokenSymbol; private String tokenDecimal; - private int transactionIndex; - private BigInteger gasPrice; - private BigInteger cumulativeGasUsed; - private long confirmations; protected TxErc20() {} // - public long getNonce() { - return nonce; - } - - public String getBlockHash() { - return blockHash; - } - public BigInteger getValue() { return value; } @@ -48,22 +34,6 @@ public String getTokenSymbol() { public String getTokenDecimal() { return tokenDecimal; } - - public int getTransactionIndex() { - return transactionIndex; - } - - public Wei getGasPrice() { - return Wei.ofWei(gasPrice); - } - - public Wei getGasUsedCumulative() { - return Wei.ofWei(cumulativeGasUsed); - } - - public long getConfirmations() { - return confirmations; - } // @Override @@ -86,18 +56,27 @@ public int hashCode() { @Override public String toString() { - return "TxERC20{" + - "nonce=" + nonce + - ", blockHash='" + blockHash + '\'' + - ", value='" + value + '\'' + + return "TxErc20{" + + "value=" + value + ", tokenName='" + tokenName + '\'' + ", tokenSymbol='" + tokenSymbol + '\'' + ", tokenDecimal='" + tokenDecimal + '\'' + + ", nonce=" + nonce + + ", blockHash='" + blockHash + '\'' + ", transactionIndex=" + transactionIndex + + ", confirmations=" + confirmations + ", gasPrice=" + gasPrice + ", cumulativeGasUsed=" + cumulativeGasUsed + - ", confirmations=" + confirmations + - "} " + super.toString(); + ", blockNumber=" + blockNumber + + ", timeStamp='" + timeStamp + '\'' + + ", hash='" + hash + '\'' + + ", from='" + from + '\'' + + ", to='" + to + '\'' + + ", contractAddress='" + contractAddress + '\'' + + ", input='" + input + '\'' + + ", gas=" + gas + + ", gasUsed=" + gasUsed + + '}'; } public static TxERC20Builder builder() { diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java b/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java index 7b59393..2180019 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java @@ -1,6 +1,5 @@ package io.goodforgod.api.etherscan.model; -import java.math.BigInteger; import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.Objects; @@ -9,30 +8,16 @@ * @author GoodforGod * @since 28.10.2018 */ -public class TxErc721 extends BaseTx { +public class TxErc721 extends BlockTx { - private long nonce; - private String blockHash; private String tokenID; private String tokenName; private String tokenSymbol; private String tokenDecimal; - private int transactionIndex; - private BigInteger gasPrice; - private BigInteger cumulativeGasUsed; - private long confirmations; protected TxErc721() {} // - public long getNonce() { - return nonce; - } - - public String getBlockHash() { - return blockHash; - } - public String getTokenID() { return tokenID; } @@ -48,22 +33,6 @@ public String getTokenSymbol() { public String getTokenDecimal() { return tokenDecimal; } - - public int getTransactionIndex() { - return transactionIndex; - } - - public Wei getGasPrice() { - return Wei.ofWei(gasPrice); - } - - public Wei getGasUsedCumulative() { - return Wei.ofWei(cumulativeGasUsed); - } - - public long getConfirmations() { - return confirmations; - } // @Override @@ -86,18 +55,27 @@ public int hashCode() { @Override public String toString() { - return "TxERC721{" + - "nonce=" + nonce + - ", blockHash='" + blockHash + '\'' + - ", tokenID='" + tokenID + '\'' + + return "TxErc721{" + + "tokenID='" + tokenID + '\'' + ", tokenName='" + tokenName + '\'' + ", tokenSymbol='" + tokenSymbol + '\'' + ", tokenDecimal='" + tokenDecimal + '\'' + + ", nonce=" + nonce + + ", blockHash='" + blockHash + '\'' + ", transactionIndex=" + transactionIndex + + ", confirmations=" + confirmations + ", gasPrice=" + gasPrice + ", cumulativeGasUsed=" + cumulativeGasUsed + - ", confirmations=" + confirmations + - "} " + super.toString(); + ", blockNumber=" + blockNumber + + ", timeStamp='" + timeStamp + '\'' + + ", hash='" + hash + '\'' + + ", from='" + from + '\'' + + ", to='" + to + '\'' + + ", contractAddress='" + contractAddress + '\'' + + ", input='" + input + '\'' + + ", gas=" + gas + + ", gasUsed=" + gasUsed + + '}'; } public static TxERC721Builder builder() { diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java b/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java index dd74e99..a61cf83 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java @@ -68,12 +68,21 @@ public int hashCode() { @Override public String toString() { return "TxInternal{" + - "type='" + type + '\'' + - ", traceId=" + traceId + - ", value=" + value + + "value=" + value + + ", type='" + type + '\'' + + ", traceId='" + traceId + '\'' + ", isError=" + isError + ", errCode='" + errCode + '\'' + - "} " + super.toString(); + ", blockNumber=" + blockNumber + + ", timeStamp='" + timeStamp + '\'' + + ", hash='" + hash + '\'' + + ", from='" + from + '\'' + + ", to='" + to + '\'' + + ", contractAddress='" + contractAddress + '\'' + + ", input='" + input + '\'' + + ", gas=" + gas + + ", gasUsed=" + gasUsed + + '}'; } public static TxInternalBuilder builder() { diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Wei.java b/src/main/java/io/goodforgod/api/etherscan/model/Wei.java index 038fd4b..3180478 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Wei.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Wei.java @@ -4,12 +4,18 @@ import java.math.BigInteger; import java.math.RoundingMode; import java.util.Objects; +import org.jetbrains.annotations.NotNull; /** * @author GoodforGod * @since 30.10.2018 */ -public class Wei { +public class Wei implements Comparable { + + private static final BigDecimal KWEI_POW = BigDecimal.ONE.pow(3); + private static final BigDecimal MWEI_POW = BigDecimal.ONE.pow(6); + private static final BigDecimal GWEI_POW = BigDecimal.ONE.pow(9); + private static final BigDecimal WEI_POW = BigDecimal.ONE.pow(18); private final BigInteger result; @@ -18,54 +24,100 @@ private Wei(BigInteger value) { } public static Wei ofWei(int value) { - return new Wei(BigInteger.valueOf(value)); + return ofWei(BigInteger.valueOf(value)); } public static Wei ofWei(long value) { - return new Wei(BigInteger.valueOf(value)); + return ofWei(BigInteger.valueOf(value)); } public static Wei ofWei(BigInteger value) { return new Wei(value); } + public static Wei ofKwei(int value) { + return ofKwei(BigInteger.valueOf(value)); + } + + public static Wei ofKwei(long value) { + return ofKwei(BigInteger.valueOf(value)); + } + + public static Wei ofKwei(BigDecimal value) { + return new Wei(value.multiply(KWEI_POW).toBigInteger()); + } + + public static Wei ofKwei(BigInteger value) { + return new Wei(value.multiply(KWEI_POW.toBigInteger())); + } + + public static Wei ofMwei(int value) { + return ofMwei(BigInteger.valueOf(value)); + } + + public static Wei ofMwei(long value) { + return ofMwei(BigInteger.valueOf(value)); + } + + public static Wei ofMwei(BigDecimal value) { + return new Wei(value.multiply(MWEI_POW).toBigInteger()); + } + + public static Wei ofMwei(BigInteger value) { + return new Wei(value.multiply(MWEI_POW.toBigInteger())); + } + + public static Wei ofGwei(int value) { + return ofGwei(BigInteger.valueOf(value)); + } + + public static Wei ofGwei(long value) { + return ofGwei(BigInteger.valueOf(value)); + } + + public static Wei ofGwei(BigDecimal value) { + return new Wei(value.multiply(GWEI_POW).toBigInteger()); + } + + public static Wei ofGwei(BigInteger value) { + return new Wei(value.multiply(GWEI_POW.toBigInteger())); + } + public static Wei ofEther(int value) { - return new Wei(BigInteger.valueOf(value).multiply(BigInteger.valueOf(1_000_000_000_000_000L))); + return ofEther(BigInteger.valueOf(value)); } public static Wei ofEther(long value) { - return new Wei(BigInteger.valueOf(value).multiply(BigInteger.valueOf(1_000_000_000_000_000L))); + return ofEther(BigInteger.valueOf(value)); } - public static Wei ofEther(BigInteger value) { - return new Wei(value.multiply(BigInteger.valueOf(1_000_000_000_000_000L))); + public static Wei ofEther(BigDecimal value) { + return new Wei(value.multiply(WEI_POW).toBigInteger()); } - public static Wei ofEther(BigDecimal value) { - return new Wei(value.multiply(BigDecimal.valueOf(1_000_000_000_000_000L)).toBigInteger()); + public static Wei ofEther(BigInteger value) { + return new Wei(value.multiply(WEI_POW.toBigInteger())); } - // public BigInteger asWei() { return result; } public BigDecimal asKwei() { - return new BigDecimal(result).divide(BigDecimal.valueOf(1_000), RoundingMode.HALF_UP); + return new BigDecimal(result).divide(KWEI_POW, RoundingMode.HALF_UP); } public BigDecimal asMwei() { - return new BigDecimal(result).divide(BigDecimal.valueOf(1_000_000), RoundingMode.HALF_UP); + return new BigDecimal(result).divide(MWEI_POW, RoundingMode.HALF_UP); } public BigDecimal asGwei() { - return new BigDecimal(result).divide(BigDecimal.valueOf(1_000_000_000), RoundingMode.HALF_UP); + return new BigDecimal(result).divide(GWEI_POW, RoundingMode.HALF_UP); } public BigDecimal asEther() { - return new BigDecimal(result).divide(BigDecimal.valueOf(1_000_000_000_000_000L), RoundingMode.HALF_UP); + return new BigDecimal(result).divide(WEI_POW, RoundingMode.HALF_UP); } - // @Override public boolean equals(Object o) { @@ -82,6 +134,11 @@ public int hashCode() { return Objects.hash(result); } + @Override + public int compareTo(@NotNull Wei o) { + return result.compareTo(o.result); + } + @Override public String toString() { return result.toString(); diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java index c98d5ee..4a2b624 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java @@ -6,12 +6,14 @@ import java.time.LocalDateTime; import java.time.ZoneOffset; import java.util.List; +import java.util.Objects; +import org.jetbrains.annotations.NotNull; /** * @author GoodforGod * @since 31.10.2018 */ -public class BlockProxy { +public class BlockProxy implements Comparable { private String number; @Expose(deserialize = false, serialize = false) @@ -145,61 +147,36 @@ public List getTransactions() { public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) + if (!(o instanceof BlockProxy)) return false; - BlockProxy that = (BlockProxy) o; - - if (number != null - ? !number.equals(that.number) - : that.number != null) - return false; - if (hash != null - ? !hash.equals(that.hash) - : that.hash != null) - return false; - return parentHash != null - ? parentHash.equals(that.parentHash) - : that.parentHash == null; + return Objects.equals(number, that.number) && Objects.equals(hash, that.hash) + && Objects.equals(parentHash, that.parentHash) && Objects.equals(nonce, that.nonce); } @Override public int hashCode() { - int result = number != null - ? number.hashCode() - : 0; - result = 31 * result + (hash != null - ? hash.hashCode() - : 0); - result = 31 * result + (parentHash != null - ? parentHash.hashCode() - : 0); - return result; + return Objects.hash(number, hash, parentHash, nonce); } @Override public String toString() { return "BlockProxy{" + "number='" + number + '\'' + - ", _number=" + _number + ", hash='" + hash + '\'' + ", parentHash='" + parentHash + '\'' + ", stateRoot='" + stateRoot + '\'' + ", size='" + size + '\'' + - ", _size=" + _size + ", difficulty='" + difficulty + '\'' + ", totalDifficulty='" + totalDifficulty + '\'' + ", timestamp='" + timestamp + '\'' + - ", _timestamp=" + _timestamp + ", miner='" + miner + '\'' + ", nonce='" + nonce + '\'' + ", extraData='" + extraData + '\'' + ", logsBloom='" + logsBloom + '\'' + ", mixHash='" + mixHash + '\'' + ", gasUsed='" + gasUsed + '\'' + - ", _gasUsed=" + _gasUsed + ", gasLimit='" + gasLimit + '\'' + - ", _gasLimit=" + _gasLimit + ", sha3Uncles='" + sha3Uncles + '\'' + ", uncles=" + uncles + ", receiptsRoot='" + receiptsRoot + '\'' + @@ -208,6 +185,11 @@ public String toString() { '}'; } + @Override + public int compareTo(@NotNull BlockProxy o) { + return Long.compare(getNumber(), o.getNumber()); + } + public static BlockProxyBuilder builder() { return new BlockProxyBuilder(); } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java index 2b616c3..e6df01c 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java @@ -5,6 +5,7 @@ import io.goodforgod.api.etherscan.model.Wei; import io.goodforgod.api.etherscan.util.BasicUtils; import java.util.List; +import java.util.Objects; /** * @author GoodforGod @@ -75,7 +76,7 @@ public Wei getGasUsed() { return _gasUsed; } - public Wei getCumulativeGasUsed() { + public Wei getGasUsedCumulative() { if (_cumulativeGasUsed == null && !BasicUtils.isEmpty(cumulativeGasUsed)) _cumulativeGasUsed = Wei.ofWei(BasicUtils.parseHex(cumulativeGasUsed)); return _cumulativeGasUsed; @@ -98,36 +99,17 @@ public String getLogsBloom() { public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) + if (!(o instanceof ReceiptProxy)) return false; - ReceiptProxy that = (ReceiptProxy) o; - - if (blockNumber != null - ? !blockNumber.equals(that.blockNumber) - : that.blockNumber != null) - return false; - if (transactionHash != null - ? !transactionHash.equals(that.transactionHash) - : that.transactionHash != null) - return false; - return transactionIndex != null - ? transactionIndex.equals(that.transactionIndex) - : that.transactionIndex == null; + return Objects.equals(blockNumber, that.blockNumber) && Objects.equals(blockHash, that.blockHash) + && Objects.equals(transactionHash, that.transactionHash) + && Objects.equals(transactionIndex, that.transactionIndex); } @Override public int hashCode() { - int result = blockNumber != null - ? blockNumber.hashCode() - : 0; - result = 31 * result + (transactionHash != null - ? transactionHash.hashCode() - : 0); - result = 31 * result + (transactionIndex != null - ? transactionIndex.hashCode() - : 0); - return result; + return Objects.hash(blockNumber, blockHash, transactionHash, transactionIndex); } @Override @@ -137,15 +119,11 @@ public String toString() { ", from='" + from + '\'' + ", to='" + to + '\'' + ", blockNumber='" + blockNumber + '\'' + - ", _blockNumber=" + _blockNumber + ", blockHash='" + blockHash + '\'' + ", transactionHash='" + transactionHash + '\'' + ", transactionIndex='" + transactionIndex + '\'' + - ", _transactionIndex=" + _transactionIndex + ", gasUsed='" + gasUsed + '\'' + - ", _gasUsed=" + _gasUsed + ", cumulativeGasUsed='" + cumulativeGasUsed + '\'' + - ", _cumulativeGasUsed=" + _cumulativeGasUsed + ", contractAddress='" + contractAddress + '\'' + ", logs=" + logs + ", logsBloom='" + logsBloom + '\'' + diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java index b2b412b..70b4fd7 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java @@ -3,12 +3,14 @@ import com.google.gson.annotations.Expose; import io.goodforgod.api.etherscan.model.Wei; import io.goodforgod.api.etherscan.util.BasicUtils; +import java.util.Objects; +import org.jetbrains.annotations.NotNull; /** * @author GoodforGod * @since 31.10.2018 */ -public class TxProxy { +public class TxProxy implements Comparable { private String to; private String hash; @@ -109,36 +111,17 @@ public Long getBlockNumber() { public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) + if (!(o instanceof TxProxy)) return false; - TxProxy txProxy = (TxProxy) o; - - if (hash != null - ? !hash.equals(txProxy.hash) - : txProxy.hash != null) - return false; - if (blockHash != null - ? !blockHash.equals(txProxy.blockHash) - : txProxy.blockHash != null) - return false; - return blockNumber != null - ? blockNumber.equals(txProxy.blockNumber) - : txProxy.blockNumber == null; + return Objects.equals(hash, txProxy.hash) && Objects.equals(transactionIndex, txProxy.transactionIndex) + && Objects.equals(nonce, txProxy.nonce) && Objects.equals(blockHash, txProxy.blockHash) + && Objects.equals(blockNumber, txProxy.blockNumber); } @Override public int hashCode() { - int result = hash != null - ? hash.hashCode() - : 0; - result = 31 * result + (blockHash != null - ? blockHash.hashCode() - : 0); - result = 31 * result + (blockNumber != null - ? blockNumber.hashCode() - : 0); - return result; + return Objects.hash(hash, transactionIndex, nonce, blockHash, blockNumber); } @Override @@ -147,25 +130,28 @@ public String toString() { "to='" + to + '\'' + ", hash='" + hash + '\'' + ", transactionIndex='" + transactionIndex + '\'' + - ", _transactionIndex=" + _transactionIndex + ", from='" + from + '\'' + ", v='" + v + '\'' + ", input='" + input + '\'' + ", s='" + s + '\'' + ", r='" + r + '\'' + ", nonce='" + nonce + '\'' + - ", _nonce=" + _nonce + ", value='" + value + '\'' + ", gas='" + gas + '\'' + - ", _gas=" + _gas + ", gasPrice='" + gasPrice + '\'' + - ", _gasPrice=" + _gasPrice + ", blockHash='" + blockHash + '\'' + ", blockNumber='" + blockNumber + '\'' + - ", _blockNumber=" + _blockNumber + '}'; } + @Override + public int compareTo(@NotNull TxProxy o) { + final int firstCompare = Long.compare(getBlockNumber(), o.getBlockNumber()); + return (firstCompare == 0) + ? Long.compare(getTransactionIndex(), o.getTransactionIndex()) + : firstCompare; + } + public static TxProxyBuilder builder() { return new TxProxyBuilder(); } diff --git a/src/test/java/io/goodforgod/api/etherscan/gastracker/GasTrackerApiTests.java b/src/test/java/io/goodforgod/api/etherscan/gastracker/GasTrackerApiTests.java index 53b1c2c..b309dd9 100644 --- a/src/test/java/io/goodforgod/api/etherscan/gastracker/GasTrackerApiTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/gastracker/GasTrackerApiTests.java @@ -1,9 +1,9 @@ package io.goodforgod.api.etherscan.gastracker; import io.goodforgod.api.etherscan.ApiRunner; -import io.goodforgod.api.etherscan.model.GasEstimate; import io.goodforgod.api.etherscan.model.GasOracle; import io.goodforgod.api.etherscan.model.Wei; +import java.time.Duration; import org.junit.jupiter.api.Test; /** @@ -14,9 +14,8 @@ class GasTrackerApiTests extends ApiRunner { @Test void estimate() { - GasEstimate estimate = getApi().gasTracker().estimate(Wei.ofWei(123)); + Duration estimate = getApi().gasTracker().estimate(Wei.ofWei(123)); assertNotNull(estimate); - assertNotNull(estimate.getDuration()); } @Test diff --git a/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java b/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java index f895595..efbb856 100644 --- a/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java @@ -74,7 +74,7 @@ void gasOracleBuilder() { .withSafeGasPrice(Wei.ofWei(1000000000)) .withGasUsedRatio(Collections.singletonList(new BigDecimal(1))) .withLastBlock(1L) - .withSuggestBaseFee(1.0) + .withSuggestBaseFee(BigDecimal.valueOf(1.0)) .build(); assertNotNull(value); @@ -86,7 +86,7 @@ void gasOracleBuilder() { .withSafeGasPrice(Wei.ofWei(1000000000)) .withGasUsedRatio(Collections.singletonList(new BigDecimal(1))) .withLastBlock(1L) - .withSuggestBaseFee(1.0) + .withSuggestBaseFee(BigDecimal.valueOf(1.0)) .build(); assertEquals(value, value2); assertEquals(value.hashCode(), value2.hashCode()); @@ -117,15 +117,15 @@ void logBuilder() { void priceBuilder() { LocalDateTime timestamp = LocalDateTime.now(); Price value = Price.builder() - .withEthBtc(1.0) - .withEthUsd(1.0) + .withEthBtc(BigDecimal.valueOf(1.0)) + .withEthUsd(BigDecimal.valueOf(1.0)) .withEthBtcTimestamp(timestamp) .withEthUsdTimestamp(timestamp) .build(); assertNotNull(value); - assertEquals(1.0, value.inUsd()); - assertEquals(1.0, value.inBtc()); + assertEquals(BigDecimal.valueOf(1.0), value.inUsd()); + assertEquals(BigDecimal.valueOf(1.0), value.inBtc()); } @Test @@ -193,6 +193,7 @@ void txBuilder() { assertEquals(value, value2); assertEquals(value.hashCode(), value2.hashCode()); assertEquals(value.toString(), value2.toString()); + assertEquals(0, value.compareTo(value2)); } @Test @@ -464,11 +465,46 @@ void blockProxyBuilder() { } @Test - void gasEstimate() { - GasEstimate gas1 = new GasEstimate(1); - GasEstimate gas2 = new GasEstimate(1); - assertEquals(gas1, gas2); - assertEquals(gas1.hashCode(), gas2.hashCode()); - assertEquals(gas1.toString(), gas2.toString()); + void weiTests() { + Wei w1 = Wei.ofWei(1); + Wei w2 = Wei.ofWei(1L); + Wei w3 = Wei.ofWei(BigInteger.valueOf(1)); + assertEquals(w1, w2); + assertEquals(w1, w3); + assertEquals(w1.hashCode(), w2.hashCode()); + assertEquals(w1.hashCode(), w3.hashCode()); + assertEquals(w1.toString(), w3.toString()); + + Wei kw1 = Wei.ofKwei(1); + Wei kw2 = Wei.ofKwei(1L); + Wei kw3 = Wei.ofKwei(BigInteger.valueOf(1)); + Wei kw4 = Wei.ofKwei(BigDecimal.valueOf(1)); + assertEquals(kw1, kw2); + assertEquals(kw1, kw3); + assertEquals(kw1, kw4); + + Wei mw1 = Wei.ofMwei(1); + Wei mw2 = Wei.ofMwei(1L); + Wei mw3 = Wei.ofMwei(BigInteger.valueOf(1)); + Wei mw4 = Wei.ofMwei(BigDecimal.valueOf(1)); + assertEquals(mw1, mw2); + assertEquals(mw1, mw3); + assertEquals(mw1, mw4); + + Wei gw1 = Wei.ofGwei(1); + Wei gw2 = Wei.ofGwei(1L); + Wei gw3 = Wei.ofGwei(BigInteger.valueOf(1)); + Wei gw4 = Wei.ofGwei(BigDecimal.valueOf(1)); + assertEquals(gw1, gw2); + assertEquals(gw1, gw3); + assertEquals(gw1, gw4); + + Wei ew1 = Wei.ofEther(1); + Wei ew2 = Wei.ofEther(1L); + Wei ew3 = Wei.ofEther(BigInteger.valueOf(1)); + Wei ew4 = Wei.ofEther(BigDecimal.valueOf(1)); + assertEquals(ew1, ew2); + assertEquals(ew1, ew3); + assertEquals(ew1, ew4); } } diff --git a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxReceiptApiTests.java b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxReceiptApiTests.java index e4322f2..662fec2 100644 --- a/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxReceiptApiTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/proxy/ProxyTxReceiptApiTests.java @@ -26,7 +26,7 @@ void correct() { assertNotNull(infoProxy.get().getTransactionHash()); assertNotNull(infoProxy.get().getTransactionIndex()); assertNotNull(infoProxy.get().getGasUsed()); - assertNotNull(infoProxy.get().getCumulativeGasUsed()); + assertNotNull(infoProxy.get().getGasUsedCumulative()); assertNotNull(infoProxy.get().getLogs()); assertNotNull(infoProxy.get().getLogsBloom()); assertNull(infoProxy.get().getContractAddress()); From e6bee19e2affac147be8d9a1aebeab1bf5f0c293 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Mon, 15 May 2023 20:58:07 +0300 Subject: [PATCH 47/61] [2.0.0] BasicProvider simplified StatisticPriceApiTests assert fixed --- .../api/etherscan/BasicProvider.java | 28 ++----------------- .../statistic/StatisticPriceApiTests.java | 4 +-- 2 files changed, 4 insertions(+), 28 deletions(-) diff --git a/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java b/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java index 3c88f3b..5c61aad 100644 --- a/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java @@ -1,6 +1,5 @@ package io.goodforgod.api.etherscan; -import io.goodforgod.api.etherscan.error.EtherScanException; import io.goodforgod.api.etherscan.error.EtherScanParseException; import io.goodforgod.api.etherscan.error.EtherScanRateLimitException; import io.goodforgod.api.etherscan.error.EtherScanResponseException; @@ -9,7 +8,6 @@ import io.goodforgod.api.etherscan.model.response.StringResponseTO; import java.net.URI; import java.nio.charset.StandardCharsets; -import java.util.Map; /** * Base provider for API Implementations @@ -64,36 +62,14 @@ T convert(byte[] json, Class tClass) { } final String jsonAsString = new String(json, StandardCharsets.UTF_8); - try { - final Map map = converter.fromJson(json, Map.class); - final Object result = map.get("result"); - if (result instanceof String && ((String) result).startsWith(MAX_RATE_LIMIT_REACHED)) - throw new EtherScanRateLimitException(((String) result)); - - throw new EtherScanParseException(e.getMessage() + ", for response: " + jsonAsString, e.getCause(), jsonAsString); - } catch (EtherScanException ex) { - throw ex; - } catch (Exception ex) { - throw new EtherScanParseException(e.getMessage() + ", for response: " + jsonAsString, e.getCause(), jsonAsString); - } + throw new EtherScanParseException(e.getMessage() + ", for response: " + jsonAsString, e.getCause(), jsonAsString); } } byte[] getRequest(String urlParameters) { queue.takeTurn(); final URI uri = URI.create(baseUrl + module + urlParameters); - final byte[] result = executor.get(uri); - if (result.length == 0) { - final StringResponseTO emptyResponse = StringResponseTO.builder() - .withStatus("0") - .withMessage("Server returned null value for GET request at URL - " + uri) - .withResult("") - .build(); - - throw new EtherScanResponseException(emptyResponse, "Server returned null value for GET request at URL - " + uri); - } - - return result; + return executor.get(uri); } byte[] postRequest(String urlParameters, String dataToPost) { diff --git a/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTests.java b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTests.java index 0dd89c2..ffc37a9 100644 --- a/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTests.java @@ -16,8 +16,8 @@ void correct() { assertNotNull(price); assertNotNull(price.btcTimestamp()); assertNotNull(price.usdTimestamp()); - assertNotEquals(0.0, price.inBtc()); - assertNotEquals(0.0, price.inUsd()); + assertNotEquals(0.0, price.inBtc().doubleValue()); + assertNotEquals(0.0, price.inUsd().doubleValue()); assertNotNull(price.toString()); Price empty = Price.builder().build(); From c64a3017f1eb99f7e4c4828de66dd9b0da0efff5 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Wed, 17 May 2023 00:04:51 +0300 Subject: [PATCH 48/61] [2.0.0] Javadoc improved --- README.md | 4 +- .../goodforgod/api/etherscan/AccountAPI.java | 62 +++++++++-------- .../api/etherscan/AccountAPIProvider.java | 68 ++++++++++--------- .../goodforgod/api/etherscan/ContractAPI.java | 2 +- .../api/etherscan/ContractAPIProvider.java | 2 +- .../io/goodforgod/api/etherscan/LogsAPI.java | 2 +- .../api/etherscan/LogsAPIProvider.java | 2 +- .../io/goodforgod/api/etherscan/ProxyAPI.java | 16 ++--- .../api/etherscan/ProxyAPIProvider.java | 16 ++--- .../api/etherscan/StatisticAPI.java | 2 +- .../api/etherscan/StatisticAPIProvider.java | 2 +- .../api/etherscan/TransactionAPI.java | 4 +- .../api/etherscan/TransactionAPIProvider.java | 4 +- .../goodforgod/api/etherscan/model/Block.java | 4 -- .../goodforgod/api/etherscan/model/Price.java | 20 +++--- .../etherscan/model/ModelBuilderTests.java | 8 +-- .../statistic/StatisticPriceApiTests.java | 4 +- 17 files changed, 114 insertions(+), 108 deletions(-) diff --git a/README.md b/README.md index dc939bf..dd244b5 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,9 @@ [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=GoodforGod_java-etherscan-api&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=GoodforGod_java-etherscan-api) [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=GoodforGod_java-etherscan-api&metric=ncloc)](https://sonarcloud.io/dashboard?id=GoodforGod_java-etherscan-api) -[Etherscan.io](https://etherscan.io/apis) Java API implementation. +[Etherscan.io](https://docs.etherscan.io/) Java API implementation. -Library supports all available EtherScan *API* calls for all available *Ethereum Networks* for *etherscan.io* +Library supports EtherScan *API* for all available *Ethereum Networks* for *etherscan.io* ## Dependency :rocket: diff --git a/src/main/java/io/goodforgod/api/etherscan/AccountAPI.java b/src/main/java/io/goodforgod/api/etherscan/AccountAPI.java index 45be8b8..09c49eb 100644 --- a/src/main/java/io/goodforgod/api/etherscan/AccountAPI.java +++ b/src/main/java/io/goodforgod/api/etherscan/AccountAPI.java @@ -21,7 +21,7 @@ public interface AccountAPI { * @throws EtherScanException parent exception class */ @NotNull - Balance balance(String address) throws EtherScanException; + Balance balance(@NotNull String address) throws EtherScanException; /** * ERC20 token balance for address @@ -32,7 +32,7 @@ public interface AccountAPI { * @throws EtherScanException parent exception class */ @NotNull - TokenBalance balance(String address, String contract) throws EtherScanException; + TokenBalance balance(@NotNull String address, @NotNull String contract) throws EtherScanException; /** * Maximum 20 address for single batch request If address MORE THAN 20, then there will be more than @@ -43,7 +43,7 @@ public interface AccountAPI { * @throws EtherScanException parent exception class */ @NotNull - List balances(List addresses) throws EtherScanException; + List balances(@NotNull List addresses) throws EtherScanException; /** * All txs for given address @@ -55,13 +55,13 @@ public interface AccountAPI { * @throws EtherScanException parent exception class */ @NotNull - List txs(String address, long startBlock, long endBlock) throws EtherScanException; + List txs(@NotNull String address, long startBlock, long endBlock) throws EtherScanException; @NotNull - List txs(String address, long startBlock) throws EtherScanException; + List txs(@NotNull String address, long startBlock) throws EtherScanException; @NotNull - List txs(String address) throws EtherScanException; + List txs(@NotNull String address) throws EtherScanException; /** * All internal txs for given address @@ -73,13 +73,13 @@ public interface AccountAPI { * @throws EtherScanException parent exception class */ @NotNull - List txsInternal(String address, long startBlock, long endBlock) throws EtherScanException; + List txsInternal(@NotNull String address, long startBlock, long endBlock) throws EtherScanException; @NotNull - List txsInternal(String address, long startBlock) throws EtherScanException; + List txsInternal(@NotNull String address, long startBlock) throws EtherScanException; @NotNull - List txsInternal(String address) throws EtherScanException; + List txsInternal(@NotNull String address) throws EtherScanException; /** * All internal tx for given transaction hash @@ -89,7 +89,7 @@ public interface AccountAPI { * @throws EtherScanException parent exception class */ @NotNull - List txsInternalByHash(String txhash) throws EtherScanException; + List txsInternalByHash(@NotNull String txhash) throws EtherScanException; /** * All ERC-20 token txs for given address @@ -101,13 +101,13 @@ public interface AccountAPI { * @throws EtherScanException parent exception class */ @NotNull - List txsErc20(String address, long startBlock, long endBlock) throws EtherScanException; + List txsErc20(@NotNull String address, long startBlock, long endBlock) throws EtherScanException; @NotNull - List txsErc20(String address, long startBlock) throws EtherScanException; + List txsErc20(@NotNull String address, long startBlock) throws EtherScanException; @NotNull - List txsErc20(String address) throws EtherScanException; + List txsErc20(@NotNull String address) throws EtherScanException; /** * All ERC-20 token txs for given address and contract address @@ -120,13 +120,14 @@ public interface AccountAPI { * @throws EtherScanException parent exception class */ @NotNull - List txsErc20(String address, String contractAddress, long startBlock, long endBlock) throws EtherScanException; + List txsErc20(@NotNull String address, @NotNull String contractAddress, long startBlock, long endBlock) + throws EtherScanException; @NotNull - List txsErc20(String address, String contractAddress, long startBlock) throws EtherScanException; + List txsErc20(@NotNull String address, @NotNull String contractAddress, long startBlock) throws EtherScanException; @NotNull - List txsErc20(String address, String contractAddress) throws EtherScanException; + List txsErc20(@NotNull String address, @NotNull String contractAddress) throws EtherScanException; /** * All ERC-721 (NFT) token txs for given address @@ -138,13 +139,13 @@ public interface AccountAPI { * @throws EtherScanException parent exception class */ @NotNull - List txsErc721(String address, long startBlock, long endBlock) throws EtherScanException; + List txsErc721(@NotNull String address, long startBlock, long endBlock) throws EtherScanException; @NotNull - List txsErc721(String address, long startBlock) throws EtherScanException; + List txsErc721(@NotNull String address, long startBlock) throws EtherScanException; @NotNull - List txsErc721(String address) throws EtherScanException; + List txsErc721(@NotNull String address) throws EtherScanException; /** * All ERC-721 (NFT) token txs for given address @@ -156,13 +157,14 @@ public interface AccountAPI { * @throws EtherScanException parent exception class */ @NotNull - List txsErc721(String address, String contractAddress, long startBlock, long endBlock) throws EtherScanException; + List txsErc721(@NotNull String address, @NotNull String contractAddress, long startBlock, long endBlock) + throws EtherScanException; @NotNull - List txsErc721(String address, String contractAddress, long startBlock) throws EtherScanException; + List txsErc721(@NotNull String address, @NotNull String contractAddress, long startBlock) throws EtherScanException; @NotNull - List txsErc721(String address, String contractAddress) throws EtherScanException; + List txsErc721(@NotNull String address, @NotNull String contractAddress) throws EtherScanException; /** * All ERC-721 (NFT) token txs for given address @@ -174,13 +176,13 @@ public interface AccountAPI { * @throws EtherScanException parent exception class */ @NotNull - List txsErc1155(String address, long startBlock, long endBlock) throws EtherScanException; + List txsErc1155(@NotNull String address, long startBlock, long endBlock) throws EtherScanException; @NotNull - List txsErc1155(String address, long startBlock) throws EtherScanException; + List txsErc1155(@NotNull String address, long startBlock) throws EtherScanException; @NotNull - List txsErc1155(String address) throws EtherScanException; + List txsErc1155(@NotNull String address) throws EtherScanException; /** * All ERC-721 (NFT) token txs for given address @@ -192,13 +194,15 @@ public interface AccountAPI { * @throws EtherScanException parent exception class */ @NotNull - List txsErc1155(String address, String contractAddress, long startBlock, long endBlock) throws EtherScanException; + List txsErc1155(@NotNull String address, @NotNull String contractAddress, long startBlock, long endBlock) + throws EtherScanException; @NotNull - List txsErc1155(String address, String contractAddress, long startBlock) throws EtherScanException; + List txsErc1155(@NotNull String address, @NotNull String contractAddress, long startBlock) + throws EtherScanException; @NotNull - List txsErc1155(String address, String contractAddress) throws EtherScanException; + List txsErc1155(@NotNull String address, @NotNull String contractAddress) throws EtherScanException; /** * All blocks mined by address @@ -208,5 +212,5 @@ public interface AccountAPI { * @throws EtherScanException parent exception class */ @NotNull - List blocksMined(String address) throws EtherScanException; + List blocksMined(@NotNull String address) throws EtherScanException; } diff --git a/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java index 08e9dd5..442edff 100644 --- a/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java @@ -56,7 +56,7 @@ final class AccountAPIProvider extends BasicProvider implements AccountAPI { @NotNull @Override - public Balance balance(String address) throws EtherScanException { + public Balance balance(@NotNull String address) throws EtherScanException { BasicUtils.validateAddress(address); final String urlParams = ACT_BALANCE_ACTION + TAG_LATEST_PARAM + ADDRESS_PARAM + address; @@ -69,7 +69,7 @@ public Balance balance(String address) throws EtherScanException { @NotNull @Override - public TokenBalance balance(String address, String contract) throws EtherScanException { + public TokenBalance balance(@NotNull String address, @NotNull String contract) throws EtherScanException { BasicUtils.validateAddress(address); BasicUtils.validateAddress(contract); @@ -83,7 +83,7 @@ public TokenBalance balance(String address, String contract) throws EtherScanExc @NotNull @Override - public List balances(List addresses) throws EtherScanException { + public List balances(@NotNull List addresses) throws EtherScanException { if (BasicUtils.isEmpty(addresses)) { return Collections.emptyList(); } @@ -117,19 +117,19 @@ private String toAddressParam(List addresses) { @NotNull @Override - public List txs(String address) throws EtherScanException { + public List txs(@NotNull String address) throws EtherScanException { return txs(address, MIN_START_BLOCK); } @NotNull @Override - public List txs(String address, long startBlock) throws EtherScanException { + public List txs(@NotNull String address, long startBlock) throws EtherScanException { return txs(address, startBlock, MAX_END_BLOCK); } @NotNull @Override - public List txs(String address, long startBlock, long endBlock) throws EtherScanException { + public List txs(@NotNull String address, long startBlock, long endBlock) throws EtherScanException { BasicUtils.validateAddress(address); final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); @@ -171,19 +171,19 @@ private > List getRequestUsingOffset(final @NotNull @Override - public List txsInternal(String address) throws EtherScanException { + public List txsInternal(@NotNull String address) throws EtherScanException { return txsInternal(address, MIN_START_BLOCK); } @NotNull @Override - public List txsInternal(String address, long startBlock) throws EtherScanException { + public List txsInternal(@NotNull String address, long startBlock) throws EtherScanException { return txsInternal(address, startBlock, MAX_END_BLOCK); } @NotNull @Override - public List txsInternal(String address, long startBlock, long endBlock) + public List txsInternal(@NotNull String address, long startBlock, long endBlock) throws EtherScanException { BasicUtils.validateAddress(address); final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); @@ -198,7 +198,7 @@ public List txsInternal(String address, long startBlock, long endBlo @NotNull @Override - public List txsInternalByHash(String txhash) throws EtherScanException { + public List txsInternalByHash(@NotNull String txhash) throws EtherScanException { BasicUtils.validateTxHash(txhash); final String urlParams = ACT_TX_INTERNAL_ACTION + TXHASH_PARAM + txhash; @@ -212,19 +212,19 @@ public List txsInternalByHash(String txhash) throws EtherScanExcepti @NotNull @Override - public List txsErc20(String address) throws EtherScanException { + public List txsErc20(@NotNull String address) throws EtherScanException { return txsErc20(address, MIN_START_BLOCK); } @NotNull @Override - public List txsErc20(String address, long startBlock) throws EtherScanException { + public List txsErc20(@NotNull String address, long startBlock) throws EtherScanException { return txsErc20(address, startBlock, MAX_END_BLOCK); } @NotNull @Override - public List txsErc20(String address, long startBlock, long endBlock) throws EtherScanException { + public List txsErc20(@NotNull String address, long startBlock, long endBlock) throws EtherScanException { BasicUtils.validateAddress(address); final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); @@ -238,19 +238,20 @@ public List txsErc20(String address, long startBlock, long endBlock) th @NotNull @Override - public List txsErc20(String address, String contractAddress) throws EtherScanException { + public List txsErc20(@NotNull String address, @NotNull String contractAddress) throws EtherScanException { return txsErc20(address, contractAddress, MIN_START_BLOCK); } @NotNull @Override - public List txsErc20(String address, String contractAddress, long startBlock) throws EtherScanException { + public List txsErc20(@NotNull String address, @NotNull String contractAddress, long startBlock) + throws EtherScanException { return txsErc20(address, contractAddress, startBlock, MAX_END_BLOCK); } @NotNull @Override - public List txsErc20(String address, String contractAddress, long startBlock, long endBlock) + public List txsErc20(@NotNull String address, @NotNull String contractAddress, long startBlock, long endBlock) throws EtherScanException { BasicUtils.validateAddress(address); final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); @@ -265,19 +266,19 @@ public List txsErc20(String address, String contractAddress, long start @NotNull @Override - public List txsErc721(String address) throws EtherScanException { + public List txsErc721(@NotNull String address) throws EtherScanException { return txsErc721(address, MIN_START_BLOCK); } @NotNull @Override - public List txsErc721(String address, long startBlock) throws EtherScanException { + public List txsErc721(@NotNull String address, long startBlock) throws EtherScanException { return txsErc721(address, startBlock, MAX_END_BLOCK); } @NotNull @Override - public List txsErc721(String address, long startBlock, long endBlock) throws EtherScanException { + public List txsErc721(@NotNull String address, long startBlock, long endBlock) throws EtherScanException { BasicUtils.validateAddress(address); final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); @@ -290,8 +291,9 @@ public List txsErc721(String address, long startBlock, long endBlock) } @Override - public @NotNull List txsErc721(String address, String contractAddress, long startBlock, long endBlock) - throws EtherScanException { + public @NotNull List + txsErc721(@NotNull String address, @NotNull String contractAddress, long startBlock, long endBlock) + throws EtherScanException { BasicUtils.validateAddress(address); final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); @@ -304,17 +306,19 @@ public List txsErc721(String address, long startBlock, long endBlock) } @Override - public @NotNull List txsErc721(String address, String contractAddress, long startBlock) throws EtherScanException { + public @NotNull List txsErc721(@NotNull String address, @NotNull String contractAddress, long startBlock) + throws EtherScanException { return txsErc721(address, contractAddress, startBlock, MAX_END_BLOCK); } @Override - public @NotNull List txsErc721(String address, String contractAddress) throws EtherScanException { + public @NotNull List txsErc721(@NotNull String address, @NotNull String contractAddress) throws EtherScanException { return txsErc721(address, contractAddress, MIN_START_BLOCK); } @Override - public @NotNull List txsErc1155(String address, long startBlock, long endBlock) throws EtherScanException { + public @NotNull List txsErc1155(@NotNull String address, long startBlock, long endBlock) + throws EtherScanException { BasicUtils.validateAddress(address); final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); @@ -327,18 +331,19 @@ public List txsErc721(String address, long startBlock, long endBlock) } @Override - public @NotNull List txsErc1155(String address, long startBlock) throws EtherScanException { + public @NotNull List txsErc1155(@NotNull String address, long startBlock) throws EtherScanException { return txsErc1155(address, startBlock, MAX_END_BLOCK); } @Override - public @NotNull List txsErc1155(String address) throws EtherScanException { + public @NotNull List txsErc1155(@NotNull String address) throws EtherScanException { return txsErc1155(address, MIN_START_BLOCK); } @Override - public @NotNull List txsErc1155(String address, String contractAddress, long startBlock, long endBlock) - throws EtherScanException { + public @NotNull List + txsErc1155(@NotNull String address, @NotNull String contractAddress, long startBlock, long endBlock) + throws EtherScanException { BasicUtils.validateAddress(address); final BlockParam blocks = BasicUtils.compensateBlocks(startBlock, endBlock); @@ -351,19 +356,20 @@ public List txsErc721(String address, long startBlock, long endBlock) } @Override - public @NotNull List txsErc1155(String address, String contractAddress, long startBlock) + public @NotNull List txsErc1155(@NotNull String address, @NotNull String contractAddress, long startBlock) throws EtherScanException { return txsErc1155(address, contractAddress, startBlock, MAX_END_BLOCK); } @Override - public @NotNull List txsErc1155(String address, String contractAddress) throws EtherScanException { + public @NotNull List txsErc1155(@NotNull String address, @NotNull String contractAddress) + throws EtherScanException { return txsErc1155(address, contractAddress, MIN_START_BLOCK); } @NotNull @Override - public List blocksMined(String address) throws EtherScanException { + public List blocksMined(@NotNull String address) throws EtherScanException { BasicUtils.validateAddress(address); final String urlParams = ACT_MINED_ACTION + PAGE_PARAM + "%s" + OFFSET_PARAM + OFFSET_MAX + BLOCK_TYPE_PARAM diff --git a/src/main/java/io/goodforgod/api/etherscan/ContractAPI.java b/src/main/java/io/goodforgod/api/etherscan/ContractAPI.java index 7564c98..af0852c 100644 --- a/src/main/java/io/goodforgod/api/etherscan/ContractAPI.java +++ b/src/main/java/io/goodforgod/api/etherscan/ContractAPI.java @@ -20,5 +20,5 @@ public interface ContractAPI { * @throws EtherScanException parent exception class */ @NotNull - Abi contractAbi(String address) throws EtherScanException; + Abi contractAbi(@NotNull String address) throws EtherScanException; } diff --git a/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java index bbb7335..6b4404a 100644 --- a/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java @@ -31,7 +31,7 @@ final class ContractAPIProvider extends BasicProvider implements ContractAPI { @NotNull @Override - public Abi contractAbi(String address) throws EtherScanException { + public Abi contractAbi(@NotNull String address) throws EtherScanException { BasicUtils.validateAddress(address); final String urlParam = ACT_ABI_PARAM + ADDRESS_PARAM + address; diff --git a/src/main/java/io/goodforgod/api/etherscan/LogsAPI.java b/src/main/java/io/goodforgod/api/etherscan/LogsAPI.java index 01d79f7..0330f9f 100644 --- a/src/main/java/io/goodforgod/api/etherscan/LogsAPI.java +++ b/src/main/java/io/goodforgod/api/etherscan/LogsAPI.java @@ -23,5 +23,5 @@ public interface LogsAPI { * @see LogQuery */ @NotNull - List logs(LogQuery query) throws EtherScanException; + List logs(@NotNull LogQuery query) throws EtherScanException; } diff --git a/src/main/java/io/goodforgod/api/etherscan/LogsAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/LogsAPIProvider.java index fe9d420..d294fb5 100644 --- a/src/main/java/io/goodforgod/api/etherscan/LogsAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/LogsAPIProvider.java @@ -31,7 +31,7 @@ final class LogsAPIProvider extends BasicProvider implements LogsAPI { @NotNull @Override - public List logs(LogQuery query) throws EtherScanException { + public List logs(@NotNull LogQuery query) throws EtherScanException { final String urlParams = ACT_LOGS_PARAM + query.params(); final LogResponseTO response = getRequest(urlParams, LogResponseTO.class); BasicUtils.validateTxResponse(response); diff --git a/src/main/java/io/goodforgod/api/etherscan/ProxyAPI.java b/src/main/java/io/goodforgod/api/etherscan/ProxyAPI.java index 77d6769..30c4f96 100644 --- a/src/main/java/io/goodforgod/api/etherscan/ProxyAPI.java +++ b/src/main/java/io/goodforgod/api/etherscan/ProxyAPI.java @@ -56,7 +56,7 @@ public interface ProxyAPI { * @throws EtherScanException parent exception class */ @NotNull - Optional tx(String txhash) throws EtherScanException; + Optional tx(@NotNull String txhash) throws EtherScanException; /** * Returns information about a transaction by block number and transaction index position @@ -87,7 +87,7 @@ public interface ProxyAPI { * @return transactions send amount from address * @throws EtherScanException parent exception class */ - int txSendCount(String address) throws EtherScanException; + int txSendCount(@NotNull String address) throws EtherScanException; /** * Creates new message call transaction or a contract creation for signed transactions @@ -98,7 +98,7 @@ public interface ProxyAPI { * @throws EtherScanException parent exception class */ @NotNull - Optional txSendRaw(String hexEncodedTx) throws EtherScanException; + Optional txSendRaw(@NotNull String hexEncodedTx) throws EtherScanException; /** * Returns the receipt of a transaction by transaction hash eth_getTransactionReceipt @@ -108,7 +108,7 @@ public interface ProxyAPI { * @throws EtherScanException parent exception class */ @NotNull - Optional txReceipt(String txhash) throws EtherScanException; + Optional txReceipt(@NotNull String txhash) throws EtherScanException; /** * Executes a new message call immediately without creating a transaction on the block chain @@ -120,7 +120,7 @@ public interface ProxyAPI { * @throws EtherScanException parent exception class */ @NotNull - Optional call(String address, String data) throws EtherScanException; + Optional call(@NotNull String address, @NotNull String data) throws EtherScanException; /** * Returns code at a given address eth_getCode @@ -130,7 +130,7 @@ public interface ProxyAPI { * @throws EtherScanException parent exception class */ @NotNull - Optional code(String address) throws EtherScanException; + Optional code(@NotNull String address) throws EtherScanException; /** * (**experimental) Returns the value from a storage position at a given address eth_getStorageAt @@ -142,7 +142,7 @@ public interface ProxyAPI { */ @Experimental @NotNull - Optional storageAt(String address, long position) throws EtherScanException; + Optional storageAt(@NotNull String address, long position) throws EtherScanException; /** * Returns the current price per gas in wei eth_gasPrice @@ -162,7 +162,7 @@ public interface ProxyAPI { * @throws EtherScanException parent exception class */ @NotNull - Wei gasEstimated(String hexData) throws EtherScanException; + Wei gasEstimated(@NotNull String hexData) throws EtherScanException; @NotNull Wei gasEstimated() throws EtherScanException; diff --git a/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java index 18edd90..4dff589 100644 --- a/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java @@ -96,7 +96,7 @@ public Optional blockUncle(long blockNo, long index) throws EtherSca @NotNull @Override - public Optional tx(String txhash) throws EtherScanException { + public Optional tx(@NotNull String txhash) throws EtherScanException { BasicUtils.validateTxHash(txhash); final String urlParams = ACT_TX_BY_HASH_PARAM + TXHASH_PARAM + txhash; @@ -127,7 +127,7 @@ public int txCount(long blockNo) throws EtherScanException { } @Override - public int txSendCount(String address) throws EtherScanException { + public int txSendCount(@NotNull String address) throws EtherScanException { BasicUtils.validateAddress(address); final String urlParams = ACT_TX_COUNT_PARAM + ADDRESS_PARAM + address + TAG_LAST_PARAM; @@ -137,7 +137,7 @@ public int txSendCount(String address) throws EtherScanException { @Override @NotNull - public Optional txSendRaw(String hexEncodedTx) throws EtherScanException { + public Optional txSendRaw(@NotNull String hexEncodedTx) throws EtherScanException { if (BasicUtils.isNotHex(hexEncodedTx)) throw new EtherScanInvalidDataHexException("Data is not encoded in hex format - " + hexEncodedTx); @@ -160,7 +160,7 @@ public Optional txSendRaw(String hexEncodedTx) throws EtherScanException @NotNull @Override - public Optional txReceipt(String txhash) throws EtherScanException { + public Optional txReceipt(@NotNull String txhash) throws EtherScanException { BasicUtils.validateTxHash(txhash); final String urlParams = ACT_TX_RECEIPT_PARAM + TXHASH_PARAM + txhash; @@ -170,7 +170,7 @@ public Optional txReceipt(String txhash) throws EtherScanException @NotNull @Override - public Optional call(String address, String data) throws EtherScanException { + public Optional call(@NotNull String address, @NotNull String data) throws EtherScanException { BasicUtils.validateAddress(address); if (BasicUtils.isNotHex(data)) throw new EtherScanInvalidDataHexException("Data is not hex encoded."); @@ -182,7 +182,7 @@ public Optional call(String address, String data) throws EtherScanExcept @NotNull @Override - public Optional code(String address) throws EtherScanException { + public Optional code(@NotNull String address) throws EtherScanException { BasicUtils.validateAddress(address); final String urlParams = ACT_CODE_PARAM + ADDRESS_PARAM + address + TAG_LAST_PARAM; @@ -192,7 +192,7 @@ public Optional code(String address) throws EtherScanException { @NotNull @Override - public Optional storageAt(String address, long position) throws EtherScanException { + public Optional storageAt(@NotNull String address, long position) throws EtherScanException { BasicUtils.validateAddress(address); final long compPosition = BasicUtils.compensateMinBlock(position); @@ -220,7 +220,7 @@ public Wei gasEstimated() throws EtherScanException { @NotNull @Override - public Wei gasEstimated(String hexData) throws EtherScanException { + public Wei gasEstimated(@NotNull String hexData) throws EtherScanException { if (!BasicUtils.isEmpty(hexData) && BasicUtils.isNotHex(hexData)) throw new EtherScanInvalidDataHexException("Data is not in hex format."); diff --git a/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java b/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java index 10e41e3..0a39eae 100644 --- a/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java +++ b/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java @@ -24,7 +24,7 @@ public interface StatisticAPI { * @throws EtherScanException parent exception class */ @NotNull - Wei supply(String contract) throws EtherScanException; + Wei supply(@NotNull String contract) throws EtherScanException; /** * Returns the current amount of Ether in circulation excluding ETH2 Staking rewards and EIP1559 diff --git a/src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java index 9555169..131df71 100644 --- a/src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java @@ -58,7 +58,7 @@ public Wei supply() throws EtherScanException { @NotNull @Override - public Wei supply(String contract) throws EtherScanException { + public Wei supply(@NotNull String contract) throws EtherScanException { BasicUtils.validateAddress(contract); final String urlParams = ACT_TOKEN_SUPPLY_PARAM + CONTRACT_ADDRESS_PARAM + contract; diff --git a/src/main/java/io/goodforgod/api/etherscan/TransactionAPI.java b/src/main/java/io/goodforgod/api/etherscan/TransactionAPI.java index a89a4a6..c719e5b 100644 --- a/src/main/java/io/goodforgod/api/etherscan/TransactionAPI.java +++ b/src/main/java/io/goodforgod/api/etherscan/TransactionAPI.java @@ -21,7 +21,7 @@ public interface TransactionAPI { * @throws EtherScanException parent exception class */ @NotNull - Optional statusExec(String txhash) throws EtherScanException; + Optional statusExec(@NotNull String txhash) throws EtherScanException; /** * Check Transaction Receipt Status (Only applicable for Post Byzantium fork transactions) @@ -31,5 +31,5 @@ public interface TransactionAPI { * @throws EtherScanException parent exception class */ @NotNull - Optional statusReceipt(String txhash) throws EtherScanException; + Optional statusReceipt(@NotNull String txhash) throws EtherScanException; } diff --git a/src/main/java/io/goodforgod/api/etherscan/TransactionAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/TransactionAPIProvider.java index c131079..da26b51 100644 --- a/src/main/java/io/goodforgod/api/etherscan/TransactionAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/TransactionAPIProvider.java @@ -33,7 +33,7 @@ final class TransactionAPIProvider extends BasicProvider implements TransactionA @NotNull @Override - public Optional statusExec(String txhash) throws EtherScanException { + public Optional statusExec(@NotNull String txhash) throws EtherScanException { BasicUtils.validateTxHash(txhash); final String urlParams = ACT_EXEC_STATUS_PARAM + TXHASH_PARAM + txhash; @@ -45,7 +45,7 @@ public Optional statusExec(String txhash) throws EtherScanException { @NotNull @Override - public Optional statusReceipt(String txhash) throws EtherScanException { + public Optional statusReceipt(@NotNull String txhash) throws EtherScanException { BasicUtils.validateTxHash(txhash); final String urlParams = ACT_RECEIPT_STATUS_PARAM + TXHASH_PARAM + txhash; diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Block.java b/src/main/java/io/goodforgod/api/etherscan/model/Block.java index 9fe4e02..0550000 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Block.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Block.java @@ -79,10 +79,6 @@ public static class BlockBuilder { BlockBuilder() {} - public static BlockBuilder aBlock() { - return new BlockBuilder(); - } - public BlockBuilder withBlockNumber(long blockNumber) { this.blockNumber = blockNumber; return this; diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Price.java b/src/main/java/io/goodforgod/api/etherscan/model/Price.java index d2a8091..565dbed 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Price.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Price.java @@ -31,14 +31,14 @@ public BigDecimal inBtc() { return ethbtc; } - public LocalDateTime usdTimestamp() { + public LocalDateTime timestampUsd() { if (_ethusd_timestamp == null && ethusd_timestamp != null) { _ethusd_timestamp = LocalDateTime.ofEpochSecond(Long.parseLong(ethusd_timestamp), 0, ZoneOffset.UTC); } return _ethusd_timestamp; } - public LocalDateTime btcTimestamp() { + public LocalDateTime timestampBtc() { if (_ethbtc_timestamp == null && ethbtc_timestamp != null) { _ethbtc_timestamp = LocalDateTime.ofEpochSecond(Long.parseLong(ethbtc_timestamp), 0, ZoneOffset.UTC); } @@ -85,23 +85,23 @@ public static final class PriceBuilder { private PriceBuilder() {} - public PriceBuilder withEthUsd(BigDecimal ethusd) { - this.ethusd = ethusd; + public PriceBuilder withUsd(BigDecimal ethToUsd) { + this.ethusd = ethToUsd; return this; } - public PriceBuilder withEthBtc(BigDecimal ethbtc) { - this.ethbtc = ethbtc; + public PriceBuilder withBtc(BigDecimal ethToBtc) { + this.ethbtc = ethToBtc; return this; } - public PriceBuilder withEthUsdTimestamp(LocalDateTime ethusdTimestamp) { - this.ethusdTimestamp = ethusdTimestamp; + public PriceBuilder withTimestampUsd(LocalDateTime ethToUsdTimestamp) { + this.ethusdTimestamp = ethToUsdTimestamp; return this; } - public PriceBuilder withEthBtcTimestamp(LocalDateTime ethbtcTimestamp) { - this.ethbtcTimestamp = ethbtcTimestamp; + public PriceBuilder withTimestampBtc(LocalDateTime ethToBtcTimestamp) { + this.ethbtcTimestamp = ethToBtcTimestamp; return this; } diff --git a/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java b/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java index efbb856..8f9a728 100644 --- a/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/model/ModelBuilderTests.java @@ -117,10 +117,10 @@ void logBuilder() { void priceBuilder() { LocalDateTime timestamp = LocalDateTime.now(); Price value = Price.builder() - .withEthBtc(BigDecimal.valueOf(1.0)) - .withEthUsd(BigDecimal.valueOf(1.0)) - .withEthBtcTimestamp(timestamp) - .withEthUsdTimestamp(timestamp) + .withBtc(BigDecimal.valueOf(1.0)) + .withUsd(BigDecimal.valueOf(1.0)) + .withTimestampBtc(timestamp) + .withTimestampUsd(timestamp) .build(); assertNotNull(value); diff --git a/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTests.java b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTests.java index ffc37a9..76b87d5 100644 --- a/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/statistic/StatisticPriceApiTests.java @@ -14,8 +14,8 @@ class StatisticPriceApiTests extends ApiRunner { void correct() { Price price = getApi().stats().priceLast(); assertNotNull(price); - assertNotNull(price.btcTimestamp()); - assertNotNull(price.usdTimestamp()); + assertNotNull(price.timestampBtc()); + assertNotNull(price.timestampUsd()); assertNotEquals(0.0, price.inBtc().doubleValue()); assertNotEquals(0.0, price.inUsd().doubleValue()); assertNotNull(price.toString()); From 0e1dcccea1c3723fe39fe40871341b2dc946265a Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Thu, 18 May 2023 01:11:42 +0300 Subject: [PATCH 49/61] [2.0.0] Javadoc fixed --- src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java b/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java index 0a39eae..d7b48b8 100644 --- a/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java +++ b/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java @@ -15,9 +15,7 @@ public interface StatisticAPI { /** - * ERC20 token total Supply - * EtherScan + * Returns the current amount of an ERC-20 token in circulation. * * @param contract contract address * @return token supply for specified contract From 333cfe4e4a08fb8c45973327370abb253a83c44b Mon Sep 17 00:00:00 2001 From: Blackmorse Date: Mon, 25 Sep 2023 00:02:09 +0400 Subject: [PATCH 50/61] Contract creation API --- .../api/etherscan/AccountAPIProvider.java | 6 +- .../goodforgod/api/etherscan/ContractAPI.java | 11 +++ .../api/etherscan/ContractAPIProvider.java | 30 +++++++ .../api/etherscan/model/ContractCreation.java | 80 +++++++++++++++++++ .../response/ContractCreationResponseTO.java | 4 + .../model/response/ContractCreationTO.java | 20 +++++ .../api/etherscan/util/BasicUtils.java | 4 + .../etherscan/contract/ContractApiTests.java | 46 +++++++++++ 8 files changed, 196 insertions(+), 5 deletions(-) create mode 100644 src/main/java/io/goodforgod/api/etherscan/model/ContractCreation.java create mode 100644 src/main/java/io/goodforgod/api/etherscan/model/response/ContractCreationResponseTO.java create mode 100644 src/main/java/io/goodforgod/api/etherscan/model/response/ContractCreationTO.java diff --git a/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java index 442edff..d36baf7 100644 --- a/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java @@ -95,7 +95,7 @@ public List balances(@NotNull List addresses) throws EtherScanE final List> addressesAsBatches = BasicUtils.partition(addresses, 20); for (final List batch : addressesAsBatches) { - final String urlParams = ACT_BALANCE_MULTI_ACTION + TAG_LATEST_PARAM + ADDRESS_PARAM + toAddressParam(batch); + final String urlParams = ACT_BALANCE_MULTI_ACTION + TAG_LATEST_PARAM + ADDRESS_PARAM + BasicUtils.toAddressParam(batch); final BalanceResponseTO response = getRequest(urlParams, BalanceResponseTO.class); if (response.getStatus() != 1) { throw new EtherScanResponseException(response); @@ -111,10 +111,6 @@ public List balances(@NotNull List addresses) throws EtherScanE return balances; } - private String toAddressParam(List addresses) { - return String.join(",", addresses); - } - @NotNull @Override public List txs(@NotNull String address) throws EtherScanException { diff --git a/src/main/java/io/goodforgod/api/etherscan/ContractAPI.java b/src/main/java/io/goodforgod/api/etherscan/ContractAPI.java index af0852c..45ecb1e 100644 --- a/src/main/java/io/goodforgod/api/etherscan/ContractAPI.java +++ b/src/main/java/io/goodforgod/api/etherscan/ContractAPI.java @@ -2,8 +2,11 @@ import io.goodforgod.api.etherscan.error.EtherScanException; import io.goodforgod.api.etherscan.model.Abi; +import io.goodforgod.api.etherscan.model.ContractCreation; import org.jetbrains.annotations.NotNull; +import java.util.List; + /** * EtherScan - API Descriptions ... * @@ -21,4 +24,12 @@ public interface ContractAPI { */ @NotNull Abi contractAbi(@NotNull String address) throws EtherScanException; + + /** + * Returns a contract's deployer address and transaction hash it was created, up to 5 at a time. + * @param contractAddresses - list of addresses to fetch + * @throws EtherScanException parent exception class + */ + @NotNull + List contractCreation(@NotNull List contractAddresses) throws EtherScanException; } diff --git a/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java index 6b4404a..fda1b0d 100644 --- a/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java @@ -5,10 +5,15 @@ import io.goodforgod.api.etherscan.http.EthHttpClient; import io.goodforgod.api.etherscan.manager.RequestQueueManager; import io.goodforgod.api.etherscan.model.Abi; +import io.goodforgod.api.etherscan.model.ContractCreation; +import io.goodforgod.api.etherscan.model.response.ContractCreationResponseTO; import io.goodforgod.api.etherscan.model.response.StringResponseTO; import io.goodforgod.api.etherscan.util.BasicUtils; import org.jetbrains.annotations.NotNull; +import java.util.List; +import java.util.stream.Collectors; + /** * Contract API Implementation * @@ -22,6 +27,12 @@ final class ContractAPIProvider extends BasicProvider implements ContractAPI { private static final String ADDRESS_PARAM = "&address="; + private static final String ACT_CONTRACT_CREATION_PARAM = "getcontractcreation"; + + private static final String ACT_CONTRACT_CREATION = ACT_PREFIX + ACT_CONTRACT_CREATION_PARAM; + + private static final String ACT_CONTRACT_ADDRESSES_PARAM = "&contractaddresses="; + ContractAPIProvider(RequestQueueManager requestQueueManager, String baseUrl, EthHttpClient executor, @@ -44,4 +55,23 @@ public Abi contractAbi(@NotNull String address) throws EtherScanException { ? Abi.nonVerified() : Abi.verified(response.getResult()); } + + @NotNull + @Override + public List contractCreation(@NotNull List contractAddresses) throws EtherScanException { + BasicUtils.validateAddresses(contractAddresses); + final String urlParam = ACT_CONTRACT_CREATION + ACT_CONTRACT_ADDRESSES_PARAM + BasicUtils.toAddressParam(contractAddresses); + final ContractCreationResponseTO response = getRequest(urlParam, ContractCreationResponseTO.class); + if (response.getStatus() != 1 && response.getMessage().startsWith("NOTOK")) { + throw new EtherScanResponseException(response); + } + + return response.getResult().stream() + .map(to -> ContractCreation.builder() + .withContractCreator(to.getContractCreator()) + .withContractAddress(to.getContractAddress()) + .withTxHash(to.getTxHash()) + .build() + ).collect(Collectors.toList()); + } } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/ContractCreation.java b/src/main/java/io/goodforgod/api/etherscan/model/ContractCreation.java new file mode 100644 index 0000000..747aefb --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/ContractCreation.java @@ -0,0 +1,80 @@ +package io.goodforgod.api.etherscan.model; + +import java.util.Objects; + +public class ContractCreation { + private final String contractAddress; + private final String contractCreator; + private final String txHash; + + private ContractCreation(String contractAddress, String contractCreator, String txHash) { + this.contractAddress = contractAddress; + this.contractCreator = contractCreator; + this.txHash = txHash; + } + + public String getContractAddress() { + return contractAddress; + } + + public String getContractCreator() { + return contractCreator; + } + + public String getTxHash() { + return txHash; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ContractCreation that = (ContractCreation) o; + return Objects.equals(contractAddress, that.contractAddress) && Objects.equals(contractCreator, that.contractCreator) && Objects.equals(txHash, that.txHash); + } + + @Override + public int hashCode() { + return Objects.hash(contractAddress, contractCreator, txHash); + } + + @Override + public String toString() { + return "ContractCreation{" + + "contractAddress='" + contractAddress + '\'' + + ", contractCreator='" + contractCreator + '\'' + + ", txHash='" + txHash + '\'' + + '}'; + } + + public static ContractCreationBuilder builder() { + return new ContractCreationBuilder(); + } + + public static final class ContractCreationBuilder { + private String contractAddress; + private String contractCreator; + private String txHash; + + private ContractCreationBuilder() {} + + public ContractCreationBuilder withContractAddress(String contractAddress) { + this.contractAddress = contractAddress; + return this; + } + + public ContractCreationBuilder withContractCreator(String contractCreator) { + this.contractCreator = contractCreator; + return this; + } + + public ContractCreationBuilder withTxHash(String txHash) { + this.txHash = txHash; + return this; + } + + public ContractCreation build() { + return new ContractCreation(contractAddress, contractCreator, txHash); + } + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/ContractCreationResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/ContractCreationResponseTO.java new file mode 100644 index 0000000..7cf28fc --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/ContractCreationResponseTO.java @@ -0,0 +1,4 @@ +package io.goodforgod.api.etherscan.model.response; + +public class ContractCreationResponseTO extends BaseListResponseTO { +} diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/ContractCreationTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/ContractCreationTO.java new file mode 100644 index 0000000..9e1551e --- /dev/null +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/ContractCreationTO.java @@ -0,0 +1,20 @@ +package io.goodforgod.api.etherscan.model.response; + +public class ContractCreationTO { + + private String contractAddress; + private String contractCreator; + private String txHash; + + public String getContractAddress() { + return contractAddress; + } + + public String getContractCreator() { + return contractCreator; + } + + public String getTxHash() { + return txHash; + } +} diff --git a/src/main/java/io/goodforgod/api/etherscan/util/BasicUtils.java b/src/main/java/io/goodforgod/api/etherscan/util/BasicUtils.java index 216ab62..916d4ab 100644 --- a/src/main/java/io/goodforgod/api/etherscan/util/BasicUtils.java +++ b/src/main/java/io/goodforgod/api/etherscan/util/BasicUtils.java @@ -149,4 +149,8 @@ public static List> partition(List list, int pairSize) { return partitioned; } + + public static String toAddressParam(List addresses) { + return String.join(",", addresses); + } } diff --git a/src/test/java/io/goodforgod/api/etherscan/contract/ContractApiTests.java b/src/test/java/io/goodforgod/api/etherscan/contract/ContractApiTests.java index 4fd0fdb..49e8f07 100644 --- a/src/test/java/io/goodforgod/api/etherscan/contract/ContractApiTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/contract/ContractApiTests.java @@ -3,8 +3,13 @@ import io.goodforgod.api.etherscan.ApiRunner; import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; import io.goodforgod.api.etherscan.model.Abi; +import io.goodforgod.api.etherscan.model.ContractCreation; import org.junit.jupiter.api.Test; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + /** * @author GoodforGod * @since 03.11.2018 @@ -37,4 +42,45 @@ void correctParamWithEmptyExpectedResult() { assertNotNull(abi); assertTrue(abi.isVerified()); } + + @Test + void correctContractCreation() { + List contractCreations = + getApi().contract().contractCreation(Collections.singletonList("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413")); + + assertEquals(1, contractCreations.size()); + ContractCreation contractCreation = contractCreations.get(0); + + assertEquals("0xbb9bc244d798123fde783fcc1c72d3bb8c189413", contractCreation.getContractAddress()); + assertEquals("0x793ea9692ada1900fbd0b80fffec6e431fe8b391", contractCreation.getContractCreator()); + assertEquals("0xe9ebfecc2fa10100db51a4408d18193b3ac504584b51a4e55bdef1318f0a30f9", contractCreation.getTxHash()); + } + + @Test + void correctMultipleContractCreation() { + List contractCreations = + getApi().contract().contractCreation(Arrays.asList("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413", "0x5EaC95ad5b287cF44E058dCf694419333b796123")); + assertEquals(2, contractCreations.size()); + + ContractCreation contractCreation1 = ContractCreation.builder() + .withContractAddress("0xbb9bc244d798123fde783fcc1c72d3bb8c189413") + .withContractCreator("0x793ea9692ada1900fbd0b80fffec6e431fe8b391") + .withTxHash("0xe9ebfecc2fa10100db51a4408d18193b3ac504584b51a4e55bdef1318f0a30f9") + .build(); + + ContractCreation contractCreation2 = ContractCreation.builder() + .withContractAddress("0x5eac95ad5b287cf44e058dcf694419333b796123") + .withContractCreator("0x7c675b7450e878e5af8550b41df42d134674e61f") + .withTxHash("0x79cdfec19e5a86d9022680a4d1c86d3d8cd76c21c01903a2f02c127a0a7dbfb3") + .build(); + + assertTrue(contractCreations.contains(contractCreation1)); + assertTrue(contractCreations.contains(contractCreation2)); + } + + @Test + void contractCreationInvalidParamWithError() { + assertThrows(EtherScanInvalidAddressException.class, + () -> getApi().contract().contractCreation(Collections.singletonList("0xBBbc244D798123fDe783fCc1C72d3Bb8C189414"))); + } } From 234cce4cadb71e1dfd45969a7709b45533d2656f Mon Sep 17 00:00:00 2001 From: Blackmorse Date: Fri, 29 Sep 2023 12:51:00 +0400 Subject: [PATCH 51/61] Formatting --- .../api/etherscan/AccountAPIProvider.java | 3 ++- .../io/goodforgod/api/etherscan/ContractAPI.java | 4 ++-- .../api/etherscan/ContractAPIProvider.java | 10 +++++----- .../io/goodforgod/api/etherscan/StatisticAPI.java | 3 +++ .../api/etherscan/model/ContractCreation.java | 11 ++++++++--- .../model/response/ContractCreationResponseTO.java | 3 +-- .../api/etherscan/contract/ContractApiTests.java | 14 +++++++------- 7 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java index d36baf7..f968c1d 100644 --- a/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java @@ -95,7 +95,8 @@ public List balances(@NotNull List addresses) throws EtherScanE final List> addressesAsBatches = BasicUtils.partition(addresses, 20); for (final List batch : addressesAsBatches) { - final String urlParams = ACT_BALANCE_MULTI_ACTION + TAG_LATEST_PARAM + ADDRESS_PARAM + BasicUtils.toAddressParam(batch); + final String urlParams = ACT_BALANCE_MULTI_ACTION + TAG_LATEST_PARAM + ADDRESS_PARAM + + BasicUtils.toAddressParam(batch); final BalanceResponseTO response = getRequest(urlParams, BalanceResponseTO.class); if (response.getStatus() != 1) { throw new EtherScanResponseException(response); diff --git a/src/main/java/io/goodforgod/api/etherscan/ContractAPI.java b/src/main/java/io/goodforgod/api/etherscan/ContractAPI.java index 45ecb1e..c076b74 100644 --- a/src/main/java/io/goodforgod/api/etherscan/ContractAPI.java +++ b/src/main/java/io/goodforgod/api/etherscan/ContractAPI.java @@ -3,9 +3,8 @@ import io.goodforgod.api.etherscan.error.EtherScanException; import io.goodforgod.api.etherscan.model.Abi; import io.goodforgod.api.etherscan.model.ContractCreation; -import org.jetbrains.annotations.NotNull; - import java.util.List; +import org.jetbrains.annotations.NotNull; /** * EtherScan - API Descriptions ... @@ -27,6 +26,7 @@ public interface ContractAPI { /** * Returns a contract's deployer address and transaction hash it was created, up to 5 at a time. + * * @param contractAddresses - list of addresses to fetch * @throws EtherScanException parent exception class */ diff --git a/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java index fda1b0d..0493f45 100644 --- a/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java @@ -9,10 +9,9 @@ import io.goodforgod.api.etherscan.model.response.ContractCreationResponseTO; import io.goodforgod.api.etherscan.model.response.StringResponseTO; import io.goodforgod.api.etherscan.util.BasicUtils; -import org.jetbrains.annotations.NotNull; - import java.util.List; import java.util.stream.Collectors; +import org.jetbrains.annotations.NotNull; /** * Contract API Implementation @@ -60,7 +59,8 @@ public Abi contractAbi(@NotNull String address) throws EtherScanException { @Override public List contractCreation(@NotNull List contractAddresses) throws EtherScanException { BasicUtils.validateAddresses(contractAddresses); - final String urlParam = ACT_CONTRACT_CREATION + ACT_CONTRACT_ADDRESSES_PARAM + BasicUtils.toAddressParam(contractAddresses); + final String urlParam = ACT_CONTRACT_CREATION + ACT_CONTRACT_ADDRESSES_PARAM + + BasicUtils.toAddressParam(contractAddresses); final ContractCreationResponseTO response = getRequest(urlParam, ContractCreationResponseTO.class); if (response.getStatus() != 1 && response.getMessage().startsWith("NOTOK")) { throw new EtherScanResponseException(response); @@ -71,7 +71,7 @@ public List contractCreation(@NotNull List contractAdd .withContractCreator(to.getContractCreator()) .withContractAddress(to.getContractAddress()) .withTxHash(to.getTxHash()) - .build() - ).collect(Collectors.toList()); + .build()) + .collect(Collectors.toList()); } } diff --git a/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java b/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java index d7b48b8..b6db82e 100644 --- a/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java +++ b/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java @@ -15,6 +15,9 @@ public interface StatisticAPI { /** + * ERC20 token total Supply + * EtherScan * Returns the current amount of an ERC-20 token in circulation. * * @param contract contract address diff --git a/src/main/java/io/goodforgod/api/etherscan/model/ContractCreation.java b/src/main/java/io/goodforgod/api/etherscan/model/ContractCreation.java index 747aefb..0f3d822 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/ContractCreation.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/ContractCreation.java @@ -3,6 +3,7 @@ import java.util.Objects; public class ContractCreation { + private final String contractAddress; private final String contractCreator; private final String txHash; @@ -27,10 +28,13 @@ public String getTxHash() { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; ContractCreation that = (ContractCreation) o; - return Objects.equals(contractAddress, that.contractAddress) && Objects.equals(contractCreator, that.contractCreator) && Objects.equals(txHash, that.txHash); + return Objects.equals(contractAddress, that.contractAddress) && Objects.equals(contractCreator, that.contractCreator) + && Objects.equals(txHash, that.txHash); } @Override @@ -52,6 +56,7 @@ public static ContractCreationBuilder builder() { } public static final class ContractCreationBuilder { + private String contractAddress; private String contractCreator; private String txHash; diff --git a/src/main/java/io/goodforgod/api/etherscan/model/response/ContractCreationResponseTO.java b/src/main/java/io/goodforgod/api/etherscan/model/response/ContractCreationResponseTO.java index 7cf28fc..e3766c3 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/response/ContractCreationResponseTO.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/response/ContractCreationResponseTO.java @@ -1,4 +1,3 @@ package io.goodforgod.api.etherscan.model.response; -public class ContractCreationResponseTO extends BaseListResponseTO { -} +public class ContractCreationResponseTO extends BaseListResponseTO {} diff --git a/src/test/java/io/goodforgod/api/etherscan/contract/ContractApiTests.java b/src/test/java/io/goodforgod/api/etherscan/contract/ContractApiTests.java index 49e8f07..d1e4de4 100644 --- a/src/test/java/io/goodforgod/api/etherscan/contract/ContractApiTests.java +++ b/src/test/java/io/goodforgod/api/etherscan/contract/ContractApiTests.java @@ -4,11 +4,10 @@ import io.goodforgod.api.etherscan.error.EtherScanInvalidAddressException; import io.goodforgod.api.etherscan.model.Abi; import io.goodforgod.api.etherscan.model.ContractCreation; -import org.junit.jupiter.api.Test; - import java.util.Arrays; import java.util.Collections; import java.util.List; +import org.junit.jupiter.api.Test; /** * @author GoodforGod @@ -45,8 +44,8 @@ void correctParamWithEmptyExpectedResult() { @Test void correctContractCreation() { - List contractCreations = - getApi().contract().contractCreation(Collections.singletonList("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413")); + List contractCreations = getApi().contract() + .contractCreation(Collections.singletonList("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413")); assertEquals(1, contractCreations.size()); ContractCreation contractCreation = contractCreations.get(0); @@ -58,8 +57,8 @@ void correctContractCreation() { @Test void correctMultipleContractCreation() { - List contractCreations = - getApi().contract().contractCreation(Arrays.asList("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413", "0x5EaC95ad5b287cF44E058dCf694419333b796123")); + List contractCreations = getApi().contract().contractCreation( + Arrays.asList("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413", "0x5EaC95ad5b287cF44E058dCf694419333b796123")); assertEquals(2, contractCreations.size()); ContractCreation contractCreation1 = ContractCreation.builder() @@ -81,6 +80,7 @@ void correctMultipleContractCreation() { @Test void contractCreationInvalidParamWithError() { assertThrows(EtherScanInvalidAddressException.class, - () -> getApi().contract().contractCreation(Collections.singletonList("0xBBbc244D798123fDe783fCc1C72d3Bb8C189414"))); + () -> getApi().contract() + .contractCreation(Collections.singletonList("0xBBbc244D798123fDe783fCc1C72d3Bb8C189414"))); } } From 64540b8499b4cfdc284d04db01ecadbbc1a1e360 Mon Sep 17 00:00:00 2001 From: Blackmorse Date: Sun, 1 Oct 2023 16:59:32 +0400 Subject: [PATCH 52/61] filtering out empty env --- src/test/java/io/goodforgod/api/etherscan/ApiRunner.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java b/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java index 4b52c00..fd933c2 100644 --- a/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java +++ b/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java @@ -2,6 +2,8 @@ import io.goodforgod.api.etherscan.manager.RequestQueueManager; import java.util.Map; + +import io.goodforgod.api.etherscan.util.BasicUtils; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; @@ -15,6 +17,7 @@ public class ApiRunner extends Assertions { static { API_KEY = System.getenv().entrySet().stream() .filter(e -> e.getKey().startsWith("ETHERSCAN_API_KEY")) + .filter(e -> !BasicUtils.isBlank(e.getValue())) .map(Map.Entry::getValue) .findFirst() .orElse(DEFAULT_KEY); From 06464f87498713c03ab27b5232586057bd066138 Mon Sep 17 00:00:00 2001 From: Blackmorse Date: Thu, 5 Oct 2023 00:58:32 +0400 Subject: [PATCH 53/61] Fix codestyle --- src/test/java/io/goodforgod/api/etherscan/ApiRunner.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java b/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java index fd933c2..a6c43ac 100644 --- a/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java +++ b/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java @@ -1,9 +1,8 @@ package io.goodforgod.api.etherscan; import io.goodforgod.api.etherscan.manager.RequestQueueManager; -import java.util.Map; - import io.goodforgod.api.etherscan.util.BasicUtils; +import java.util.Map; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; From 3a87ca83cda95a61415bc5226651b5d18448900f Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Fri, 6 Oct 2023 02:10:05 +0300 Subject: [PATCH 54/61] [2.1.0-SNAPSHOT] EtherScanAPI.Builder#withRetryOnLimitReach added --- gradle.properties | 2 +- .../api/etherscan/AccountAPIProvider.java | 5 +- .../api/etherscan/BasicProvider.java | 51 ++++++++++++++++--- .../api/etherscan/BlockAPIProvider.java | 12 ++--- .../api/etherscan/ContractAPIProvider.java | 5 +- .../api/etherscan/EthScanAPIBuilder.java | 13 ++++- .../api/etherscan/EtherScanAPI.java | 11 ++++ .../api/etherscan/EtherScanAPIProvider.java | 19 +++---- .../api/etherscan/GasTrackerAPIProvider.java | 5 +- .../api/etherscan/LogsAPIProvider.java | 5 +- .../api/etherscan/ProxyAPIProvider.java | 5 +- .../api/etherscan/StatisticAPIProvider.java | 5 +- .../api/etherscan/TransactionAPIProvider.java | 5 +- .../manager/RequestQueueManager.java | 10 ++-- .../goodforgod/api/etherscan/model/Abi.java | 2 +- .../api/etherscan/model/Balance.java | 2 +- .../goodforgod/api/etherscan/model/Block.java | 2 +- .../api/etherscan/model/BlockUncle.java | 8 +-- .../api/etherscan/model/ContractCreation.java | 29 ++++++----- .../api/etherscan/model/EthSupply.java | 8 +-- .../goodforgod/api/etherscan/model/Log.java | 18 +++---- .../goodforgod/api/etherscan/model/Price.java | 4 +- .../api/etherscan/model/Status.java | 2 +- .../api/etherscan/model/TokenBalance.java | 2 +- .../io/goodforgod/api/etherscan/model/Tx.java | 18 +++---- .../api/etherscan/model/TxErc1155.java | 22 ++++---- .../api/etherscan/model/TxErc20.java | 20 ++++---- .../api/etherscan/model/TxErc721.java | 22 ++++---- .../api/etherscan/model/TxInternal.java | 18 +++---- .../api/etherscan/model/proxy/BlockProxy.java | 36 ++++++------- .../etherscan/model/proxy/ReceiptProxy.java | 22 ++++---- .../api/etherscan/model/proxy/TxProxy.java | 28 +++++----- .../goodforgod/api/etherscan/ApiRunner.java | 1 + 33 files changed, 242 insertions(+), 175 deletions(-) diff --git a/gradle.properties b/gradle.properties index 821da06..ee5fd3b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ groupId=com.github.goodforgod artifactId=java-etherscan-api -artifactVersion=2.0.0 +artifactVersion=2.1.0-SNAPSHOT ##### GRADLE ##### diff --git a/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java index f968c1d..750d525 100644 --- a/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/AccountAPIProvider.java @@ -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 diff --git a/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java b/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java index 5c61aad..41abd16 100644 --- a/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/BasicProvider.java @@ -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 convert(byte[] json, Class tClass) { + private T convert(byte[] json, Class tClass) { try { final T t = converter.fromJson(json, tClass); if (t instanceof StringResponseTO && ((StringResponseTO) t).getResult().startsWith(MAX_RATE_LIMIT_REACHED)) { @@ -66,23 +69,59 @@ T convert(byte[] json, Class 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 getRequest(String urlParameters, Class tClass) { - return convert(getRequest(urlParameters), tClass); + return getRequest(urlParameters, tClass, 0); + } + + private T getRequest(String urlParameters, Class 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 postRequest(String urlParameters, String dataToPost, Class tClass) { - return convert(postRequest(urlParameters, dataToPost), tClass); + return postRequest(urlParameters, dataToPost, tClass, 0); + } + + private T postRequest(String urlParameters, String dataToPost, Class 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; + } + } } } diff --git a/src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java index 406ac19..b3604a7 100644 --- a/src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/BlockAPIProvider.java @@ -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 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(); } diff --git a/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java index 0493f45..898a7b7 100644 --- a/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/ContractAPIProvider.java @@ -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 diff --git a/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java b/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java index dad9c50..70d9a01 100644 --- a/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java +++ b/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java @@ -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; @@ -87,6 +88,16 @@ public EtherScanAPI.Builder withConverter(@NotNull Supplier 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; @@ -99,6 +110,6 @@ public EtherScanAPI.Builder withConverter(@NotNull Supplier converter } return new EtherScanAPIProvider(apiKey, ethNetwork, requestQueueManager, ethHttpClientSupplier.get(), - converterSupplier.get()); + converterSupplier.get(), retryCountOnLimitReach); } } diff --git a/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java index 6da3d8f..4e6bc57 100644 --- a/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java +++ b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java @@ -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 ... @@ -62,6 +64,15 @@ interface Builder { @NotNull Builder withConverter(@NotNull Supplier 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(); } diff --git a/src/main/java/io/goodforgod/api/etherscan/EtherScanAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPIProvider.java index e698f45..ab6e863 100644 --- a/src/main/java/io/goodforgod/api/etherscan/EtherScanAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPIProvider.java @@ -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 diff --git a/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java index cbe0a75..ed717a9 100644 --- a/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/GasTrackerAPIProvider.java @@ -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 diff --git a/src/main/java/io/goodforgod/api/etherscan/LogsAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/LogsAPIProvider.java index d294fb5..237cafd 100644 --- a/src/main/java/io/goodforgod/api/etherscan/LogsAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/LogsAPIProvider.java @@ -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 diff --git a/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java index 4dff589..428b48f 100644 --- a/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/ProxyAPIProvider.java @@ -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 diff --git a/src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java index 131df71..a2bba16 100644 --- a/src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/StatisticAPIProvider.java @@ -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 diff --git a/src/main/java/io/goodforgod/api/etherscan/TransactionAPIProvider.java b/src/main/java/io/goodforgod/api/etherscan/TransactionAPIProvider.java index da26b51..7374335 100644 --- a/src/main/java/io/goodforgod/api/etherscan/TransactionAPIProvider.java +++ b/src/main/java/io/goodforgod/api/etherscan/TransactionAPIProvider.java @@ -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 diff --git a/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java b/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java index 0f36b23..92875d0 100644 --- a/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java +++ b/src/main/java/io/goodforgod/api/etherscan/manager/RequestQueueManager.java @@ -17,7 +17,7 @@ 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)); } /** @@ -25,19 +25,19 @@ static RequestQueueManager anonymous() { * Free API KEY */ 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() { diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Abi.java b/src/main/java/io/goodforgod/api/etherscan/model/Abi.java index 3536bf9..fbf71be 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Abi.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Abi.java @@ -55,7 +55,7 @@ public int hashCode() { @Override public String toString() { return "Abi{" + - "contractAbi='" + contractAbi + '\'' + + "contractAbi=" + contractAbi + ", isVerified=" + isVerified + '}'; } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Balance.java b/src/main/java/io/goodforgod/api/etherscan/model/Balance.java index 079d4b6..1d2f743 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Balance.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Balance.java @@ -45,7 +45,7 @@ public int hashCode() { @Override public String toString() { return "Balance{" + - "address='" + address + '\'' + + "address=" + address + ", balance=" + balance + '}'; } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Block.java b/src/main/java/io/goodforgod/api/etherscan/model/Block.java index 0550000..da1184b 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Block.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Block.java @@ -58,7 +58,7 @@ public String toString() { return "Block{" + "blockNumber=" + blockNumber + ", blockReward=" + blockReward + - ", timeStamp='" + timeStamp + '\'' + + ", timeStamp=" + timeStamp + '}'; } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java b/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java index 9b110d9..961db7e 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/BlockUncle.java @@ -19,7 +19,7 @@ public static class Uncle { private BigInteger blockreward; private int unclePosition; - private Uncle() {} + protected Uncle() {} // public String getMiner() { @@ -54,7 +54,7 @@ public int hashCode() { @Override public String toString() { return "Uncle{" + - "miner='" + miner + '\'' + + "miner=" + miner + ", blockreward=" + blockreward + ", unclePosition=" + unclePosition + '}'; @@ -128,9 +128,9 @@ public String getUncleInclusionReward() { @Override public String toString() { return "UncleBlock{" + - "blockMiner='" + blockMiner + '\'' + + "blockMiner=" + blockMiner + ", uncles=" + uncles + - ", uncleInclusionReward='" + uncleInclusionReward + '\'' + + ", uncleInclusionReward=" + uncleInclusionReward + '}'; } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/ContractCreation.java b/src/main/java/io/goodforgod/api/etherscan/model/ContractCreation.java index 0f3d822..2082883 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/ContractCreation.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/ContractCreation.java @@ -4,15 +4,11 @@ public class ContractCreation { - private final String contractAddress; - private final String contractCreator; - private final String txHash; - - private ContractCreation(String contractAddress, String contractCreator, String txHash) { - this.contractAddress = contractAddress; - this.contractCreator = contractCreator; - this.txHash = txHash; - } + private String contractAddress; + private String contractCreator; + private String txHash; + + protected ContractCreation() {} public String getContractAddress() { return contractAddress; @@ -33,7 +29,8 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; ContractCreation that = (ContractCreation) o; - return Objects.equals(contractAddress, that.contractAddress) && Objects.equals(contractCreator, that.contractCreator) + return Objects.equals(contractAddress, that.contractAddress) + && Objects.equals(contractCreator, that.contractCreator) && Objects.equals(txHash, that.txHash); } @@ -45,9 +42,9 @@ public int hashCode() { @Override public String toString() { return "ContractCreation{" + - "contractAddress='" + contractAddress + '\'' + - ", contractCreator='" + contractCreator + '\'' + - ", txHash='" + txHash + '\'' + + "contractAddress=" + contractAddress + + ", contractCreator=" + contractCreator + + ", txHash=" + txHash + '}'; } @@ -79,7 +76,11 @@ public ContractCreationBuilder withTxHash(String txHash) { } public ContractCreation build() { - return new ContractCreation(contractAddress, contractCreator, txHash); + ContractCreation contractCreation = new ContractCreation(); + contractCreation.contractAddress = contractAddress; + contractCreation.contractCreator = contractCreator; + contractCreation.txHash = txHash; + return contractCreation; } } } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/EthSupply.java b/src/main/java/io/goodforgod/api/etherscan/model/EthSupply.java index 344e754..c626069 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/EthSupply.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/EthSupply.java @@ -56,10 +56,10 @@ public int hashCode() { @Override public String toString() { return "EthSupply{" + - "EthSupply='" + EthSupply + '\'' + - ", Eth2Staking='" + Eth2Staking + '\'' + - ", BurntFees='" + BurntFees + '\'' + - ", WithdrawnTotal='" + WithdrawnTotal + '\'' + + "EthSupply=" + EthSupply + + ", Eth2Staking=" + Eth2Staking + + ", BurntFees=" + BurntFees + + ", WithdrawnTotal=" + WithdrawnTotal + '}'; } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Log.java b/src/main/java/io/goodforgod/api/etherscan/model/Log.java index da6c295..d54766c 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Log.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Log.java @@ -138,16 +138,16 @@ public int hashCode() { @Override public String toString() { return "Log{" + - "blockNumber='" + blockNumber + '\'' + - ", address='" + address + '\'' + - ", transactionHash='" + transactionHash + '\'' + - ", transactionIndex='" + transactionIndex + '\'' + - ", timeStamp='" + timeStamp + '\'' + - ", data='" + data + '\'' + - ", gasPrice='" + gasPrice + '\'' + - ", gasUsed='" + gasUsed + '\'' + + "blockNumber=" + blockNumber + + ", address=" + address + + ", transactionHash=" + transactionHash + + ", transactionIndex=" + transactionIndex + + ", timeStamp=" + timeStamp + + ", data=" + data + + ", gasPrice=" + gasPrice + + ", gasUsed=" + gasUsed + ", topics=" + topics + - ", logIndex='" + logIndex + '\'' + + ", logIndex=" + logIndex + '}'; } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Price.java b/src/main/java/io/goodforgod/api/etherscan/model/Price.java index 565dbed..403b705 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Price.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Price.java @@ -67,8 +67,8 @@ public String toString() { return "Price{" + "ethusd=" + ethusd + ", ethbtc=" + ethbtc + - ", ethusd_timestamp='" + ethusd_timestamp + '\'' + - ", ethbtc_timestamp='" + ethbtc_timestamp + '\'' + + ", ethusd_timestamp=" + ethusd_timestamp + + ", ethbtc_timestamp=" + ethbtc_timestamp + '}'; } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Status.java b/src/main/java/io/goodforgod/api/etherscan/model/Status.java index 052c187..41b598a 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Status.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Status.java @@ -45,7 +45,7 @@ public int hashCode() { public String toString() { return "Status{" + "isError=" + isError + - ", errDescription='" + errDescription + '\'' + + ", errDescription=" + errDescription + '}'; } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TokenBalance.java b/src/main/java/io/goodforgod/api/etherscan/model/TokenBalance.java index bb40ee2..c257654 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TokenBalance.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TokenBalance.java @@ -39,7 +39,7 @@ public int hashCode() { @Override public String toString() { return "TokenBalance{" + - "tokenContract='" + tokenContract + '\'' + + "tokenContract=" + tokenContract + '}'; } } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/Tx.java b/src/main/java/io/goodforgod/api/etherscan/model/Tx.java index 7ef0e22..0a836d1 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/Tx.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/Tx.java @@ -35,21 +35,21 @@ public String getTxReceiptStatus() { public String toString() { return "Tx{" + "value=" + value + - ", isError='" + isError + '\'' + - ", txreceipt_status='" + txreceipt_status + '\'' + + ", isError=" + isError + + ", txreceipt_status=" + txreceipt_status + ", nonce=" + nonce + - ", blockHash='" + blockHash + '\'' + + ", blockHash=" + blockHash + ", transactionIndex=" + transactionIndex + ", confirmations=" + confirmations + ", gasPrice=" + gasPrice + ", cumulativeGasUsed=" + cumulativeGasUsed + ", blockNumber=" + blockNumber + - ", timeStamp='" + timeStamp + '\'' + - ", hash='" + hash + '\'' + - ", from='" + from + '\'' + - ", to='" + to + '\'' + - ", contractAddress='" + contractAddress + '\'' + - ", input='" + input + '\'' + + ", timeStamp=" + timeStamp + + ", hash=" + hash + + ", from=" + from + + ", to=" + to + + ", contractAddress=" + contractAddress + + ", input=" + input + ", gas=" + gas + ", gasUsed=" + gasUsed + '}'; diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java b/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java index 16d4457..f0b1ce4 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxErc1155.java @@ -56,23 +56,23 @@ public int hashCode() { @Override public String toString() { return "TxErc1155{" + - "tokenID='" + tokenID + '\'' + - ", tokenName='" + tokenName + '\'' + - ", tokenSymbol='" + tokenSymbol + '\'' + - ", tokenValue='" + tokenValue + '\'' + + "tokenID=" + tokenID + + ", tokenName=" + tokenName + + ", tokenSymbol=" + tokenSymbol + + ", tokenValue=" + tokenValue + ", nonce=" + nonce + - ", blockHash='" + blockHash + '\'' + + ", blockHash=" + blockHash + ", transactionIndex=" + transactionIndex + ", confirmations=" + confirmations + ", gasPrice=" + gasPrice + ", cumulativeGasUsed=" + cumulativeGasUsed + ", blockNumber=" + blockNumber + - ", timeStamp='" + timeStamp + '\'' + - ", hash='" + hash + '\'' + - ", from='" + from + '\'' + - ", to='" + to + '\'' + - ", contractAddress='" + contractAddress + '\'' + - ", input='" + input + '\'' + + ", timeStamp=" + timeStamp + + ", hash=" + hash + + ", from=" + from + + ", to=" + to + + ", contractAddress=" + contractAddress + + ", input=" + input + ", gas=" + gas + ", gasUsed=" + gasUsed + '}'; diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java b/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java index 3dc22fd..1d6080e 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxErc20.java @@ -58,22 +58,22 @@ public int hashCode() { public String toString() { return "TxErc20{" + "value=" + value + - ", tokenName='" + tokenName + '\'' + - ", tokenSymbol='" + tokenSymbol + '\'' + - ", tokenDecimal='" + tokenDecimal + '\'' + + ", tokenName=" + tokenName + + ", tokenSymbol=" + tokenSymbol + + ", tokenDecimal=" + tokenDecimal + ", nonce=" + nonce + - ", blockHash='" + blockHash + '\'' + + ", blockHash=" + blockHash + ", transactionIndex=" + transactionIndex + ", confirmations=" + confirmations + ", gasPrice=" + gasPrice + ", cumulativeGasUsed=" + cumulativeGasUsed + ", blockNumber=" + blockNumber + - ", timeStamp='" + timeStamp + '\'' + - ", hash='" + hash + '\'' + - ", from='" + from + '\'' + - ", to='" + to + '\'' + - ", contractAddress='" + contractAddress + '\'' + - ", input='" + input + '\'' + + ", timeStamp=" + timeStamp + + ", hash=" + hash + + ", from=" + from + + ", to=" + to + + ", contractAddress=" + contractAddress + + ", input=" + input + ", gas=" + gas + ", gasUsed=" + gasUsed + '}'; diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java b/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java index 2180019..1ac49a0 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxErc721.java @@ -56,23 +56,23 @@ public int hashCode() { @Override public String toString() { return "TxErc721{" + - "tokenID='" + tokenID + '\'' + - ", tokenName='" + tokenName + '\'' + - ", tokenSymbol='" + tokenSymbol + '\'' + - ", tokenDecimal='" + tokenDecimal + '\'' + + "tokenID=" + tokenID + + ", tokenName=" + tokenName + + ", tokenSymbol=" + tokenSymbol + + ", tokenDecimal=" + tokenDecimal + ", nonce=" + nonce + - ", blockHash='" + blockHash + '\'' + + ", blockHash=" + blockHash + ", transactionIndex=" + transactionIndex + ", confirmations=" + confirmations + ", gasPrice=" + gasPrice + ", cumulativeGasUsed=" + cumulativeGasUsed + ", blockNumber=" + blockNumber + - ", timeStamp='" + timeStamp + '\'' + - ", hash='" + hash + '\'' + - ", from='" + from + '\'' + - ", to='" + to + '\'' + - ", contractAddress='" + contractAddress + '\'' + - ", input='" + input + '\'' + + ", timeStamp=" + timeStamp + + ", hash=" + hash + + ", from=" + from + + ", to=" + to + + ", contractAddress=" + contractAddress + + ", input=" + input + ", gas=" + gas + ", gasUsed=" + gasUsed + '}'; diff --git a/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java b/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java index a61cf83..389f456 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/TxInternal.java @@ -69,17 +69,17 @@ public int hashCode() { public String toString() { return "TxInternal{" + "value=" + value + - ", type='" + type + '\'' + - ", traceId='" + traceId + '\'' + + ", type=" + type + + ", traceId=" + traceId + ", isError=" + isError + - ", errCode='" + errCode + '\'' + + ", errCode=" + errCode + ", blockNumber=" + blockNumber + - ", timeStamp='" + timeStamp + '\'' + - ", hash='" + hash + '\'' + - ", from='" + from + '\'' + - ", to='" + to + '\'' + - ", contractAddress='" + contractAddress + '\'' + - ", input='" + input + '\'' + + ", timeStamp=" + timeStamp + + ", hash=" + hash + + ", from=" + from + + ", to=" + to + + ", contractAddress=" + contractAddress + + ", input=" + input + ", gas=" + gas + ", gasUsed=" + gasUsed + '}'; diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java index 4a2b624..bee4d64 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/BlockProxy.java @@ -162,25 +162,25 @@ public int hashCode() { @Override public String toString() { return "BlockProxy{" + - "number='" + number + '\'' + - ", hash='" + hash + '\'' + - ", parentHash='" + parentHash + '\'' + - ", stateRoot='" + stateRoot + '\'' + - ", size='" + size + '\'' + - ", difficulty='" + difficulty + '\'' + - ", totalDifficulty='" + totalDifficulty + '\'' + - ", timestamp='" + timestamp + '\'' + - ", miner='" + miner + '\'' + - ", nonce='" + nonce + '\'' + - ", extraData='" + extraData + '\'' + - ", logsBloom='" + logsBloom + '\'' + - ", mixHash='" + mixHash + '\'' + - ", gasUsed='" + gasUsed + '\'' + - ", gasLimit='" + gasLimit + '\'' + - ", sha3Uncles='" + sha3Uncles + '\'' + + "number=" + number + + ", hash=" + hash + + ", parentHash=" + parentHash + + ", stateRoot=" + stateRoot + + ", size=" + size + + ", difficulty=" + difficulty + + ", totalDifficulty=" + totalDifficulty + + ", timestamp=" + timestamp + + ", miner=" + miner + + ", nonce=" + nonce + + ", extraData=" + extraData + + ", logsBloom=" + logsBloom + + ", mixHash=" + mixHash + + ", gasUsed=" + gasUsed + + ", gasLimit=" + gasLimit + + ", sha3Uncles=" + sha3Uncles + ", uncles=" + uncles + - ", receiptsRoot='" + receiptsRoot + '\'' + - ", transactionsRoot='" + transactionsRoot + '\'' + + ", receiptsRoot=" + receiptsRoot + + ", transactionsRoot=" + transactionsRoot + ", transactions=" + transactions + '}'; } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java index e6df01c..d88fd6d 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/ReceiptProxy.java @@ -115,18 +115,18 @@ public int hashCode() { @Override public String toString() { return "ReceiptProxy{" + - "root='" + root + '\'' + - ", from='" + from + '\'' + - ", to='" + to + '\'' + - ", blockNumber='" + blockNumber + '\'' + - ", blockHash='" + blockHash + '\'' + - ", transactionHash='" + transactionHash + '\'' + - ", transactionIndex='" + transactionIndex + '\'' + - ", gasUsed='" + gasUsed + '\'' + - ", cumulativeGasUsed='" + cumulativeGasUsed + '\'' + - ", contractAddress='" + contractAddress + '\'' + + "root=" + root + + ", from=" + from + + ", to=" + to + + ", blockNumber=" + blockNumber + + ", blockHash=" + blockHash + + ", transactionHash=" + transactionHash + + ", transactionIndex=" + transactionIndex + + ", gasUsed=" + gasUsed + + ", cumulativeGasUsed=" + cumulativeGasUsed + + ", contractAddress=" + contractAddress + ", logs=" + logs + - ", logsBloom='" + logsBloom + '\'' + + ", logsBloom=" + logsBloom + '}'; } diff --git a/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java b/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java index 70b4fd7..0a89921 100644 --- a/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java +++ b/src/main/java/io/goodforgod/api/etherscan/model/proxy/TxProxy.java @@ -127,20 +127,20 @@ public int hashCode() { @Override public String toString() { return "TxProxy{" + - "to='" + to + '\'' + - ", hash='" + hash + '\'' + - ", transactionIndex='" + transactionIndex + '\'' + - ", from='" + from + '\'' + - ", v='" + v + '\'' + - ", input='" + input + '\'' + - ", s='" + s + '\'' + - ", r='" + r + '\'' + - ", nonce='" + nonce + '\'' + - ", value='" + value + '\'' + - ", gas='" + gas + '\'' + - ", gasPrice='" + gasPrice + '\'' + - ", blockHash='" + blockHash + '\'' + - ", blockNumber='" + blockNumber + '\'' + + "to=" + to + + ", hash=" + hash + + ", transactionIndex=" + transactionIndex + + ", from=" + from + + ", v=" + v + + ", input=" + input + + ", s=" + s + + ", r=" + r + + ", nonce=" + nonce + + ", value=" + value + + ", gas=" + gas + + ", gasPrice=" + gasPrice + + ", blockHash=" + blockHash + + ", blockNumber=" + blockNumber + '}'; } diff --git a/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java b/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java index a6c43ac..72aeeff 100644 --- a/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java +++ b/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java @@ -29,6 +29,7 @@ public class ApiRunner extends Assertions { .withApiKey(ApiRunner.API_KEY) .withNetwork(EthNetworks.MAINNET) .withQueue(queueManager) + .withRetryOnLimitReach(5) .build(); } From 3405883f524a98b8c5c5848ddd862b140cac28aa Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Fri, 6 Oct 2023 02:10:53 +0300 Subject: [PATCH 55/61] [2.1.0-SNAPSHOT] build.gradle updated for new CI CONTRIBUTING.md added README.md updated --- CONTRIBUTING.md | 23 +++++++++++ README.md | 7 ++-- _config.yml | 1 - build.gradle | 48 +++++++++++++++++++---- gradle/wrapper/gradle-wrapper.jar | Bin 59536 -> 61574 bytes gradle/wrapper/gradle-wrapper.properties | 3 +- gradlew | 18 +++++++-- gradlew.bat | 15 ++++--- 8 files changed, 92 insertions(+), 23 deletions(-) create mode 100644 CONTRIBUTING.md delete mode 100644 _config.yml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..5abd8dc --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,23 @@ +# Contributing Code or Documentation Guide + +## Running Tests + +The new code should contain tests that check new behavior. + +Run tests `./gradlew test` to check that code works as behavior. + +## Code Style + +The code base should remain clean, following industry best practices for organization, javadoc and style, as much as possible. + +To run the Code Style check use `./gradlew spotlessCheck`. + +If check found any errors, you can apply Code Style by running `./gradlew spotlessApply` + +## Creating a pull request + +Once you are satisfied with your changes: + +- Commit changes to the local branch you created. +- Push that branch with changes to the corresponding remote branch on GitHub +- Submit a [pull request](https://help.github.com/articles/creating-a-pull-request) to `dev` branch. \ No newline at end of file diff --git a/README.md b/README.md index dd244b5..c086a6b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # Java EtherScan API [![Minimum required Java version](https://img.shields.io/badge/Java-1.8%2B-blue?logo=openjdk)](https://openjdk.org/projects/jdk8/) -[![GitHub Action](https://github.com/goodforgod/java-etherscan-api/workflows/Java%20CI/badge.svg)](https://github.com/GoodforGod/java-etherscan-api/actions?query=workflow%3A%22Java+CI%22) +[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.goodforgod/java-etherscan-api/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.github.goodforgod/java-etherscan-api) +[![Java CI](https://github.com/GoodforGod/java-etherscan-api/workflows/CI%20Master/badge.svg)](https://github.com/GoodforGod/java-etherscan-api/actions?query=workflow%3ACI+Master) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=GoodforGod_java-etherscan-api&metric=coverage)](https://sonarcloud.io/dashboard?id=GoodforGod_java-etherscan-api) [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=GoodforGod_java-etherscan-api&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=GoodforGod_java-etherscan-api) [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=GoodforGod_java-etherscan-api&metric=ncloc)](https://sonarcloud.io/dashboard?id=GoodforGod_java-etherscan-api) @@ -14,7 +15,7 @@ Library supports EtherScan *API* for all available *Ethereum Networks* for *ethe **Gradle** ```groovy -implementation "com.github.goodforgod:java-etherscan-api:2.0.0" +implementation "com.github.goodforgod:java-etherscan-api:2.1.0" ``` **Maven** @@ -22,7 +23,7 @@ implementation "com.github.goodforgod:java-etherscan-api:2.0.0" com.github.goodforgod java-etherscan-api - 2.0.0 + 2.1.0 ``` diff --git a/_config.yml b/_config.yml deleted file mode 100644 index 2f7efbe..0000000 --- a/_config.yml +++ /dev/null @@ -1 +0,0 @@ -theme: jekyll-theme-minimal \ No newline at end of file diff --git a/build.gradle b/build.gradle index 3d766c2..7dcf4c7 100644 --- a/build.gradle +++ b/build.gradle @@ -3,8 +3,9 @@ plugins { id "java-library" id "maven-publish" - id "org.sonarqube" version "3.3" - id "com.diffplug.spotless" version "6.12.0" + id "org.sonarqube" version "4.3.0.3225" + id "com.diffplug.spotless" version "6.19.0" + id "io.github.gradle-nexus.publish-plugin" version "1.3.0" } repositories { @@ -13,7 +14,8 @@ repositories { } group = groupId -version = artifactVersion +var ver = System.getenv().getOrDefault("RELEASE_VERSION", artifactVersion) +version = ver.startsWith("v") ? ver.substring(1) : ver sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 @@ -28,6 +30,7 @@ dependencies { } test { + failFast(false) useJUnitPlatform() testLogging { events("passed", "skipped", "failed") @@ -36,9 +39,13 @@ test { } reports { - html.enabled(false) - junitXml.enabled(false) + html.required = false + junitXml.required = false } + + environment([ + "": "", + ]) } spotless { @@ -46,7 +53,7 @@ spotless { encoding("UTF-8") importOrder() removeUnusedImports() - eclipse("4.21.0").configFile("${rootDir}/config/codestyle.xml") + eclipse("4.21").configFile("${rootDir}/config/codestyle.xml") } } @@ -58,6 +65,18 @@ sonarqube { } } +nexusPublishing { + packageGroup = groupId + repositories { + sonatype { + username = System.getenv("OSS_USERNAME") + password = System.getenv("OSS_PASSWORD") + nexusUrl.set(uri("https://oss.sonatype.org/service/local/")) + snapshotRepositoryUrl.set(uri("https://oss.sonatype.org/content/repositories/snapshots/")) + } + } +} + publishing { publications { mavenJava(MavenPublication) { @@ -99,6 +118,16 @@ publishing { password System.getenv("OSS_PASSWORD") } } + if (!version.endsWith("SNAPSHOT")) { + maven { + name = "GitHubPackages" + url = "https://maven.pkg.github.com/GoodforGod/$artifactId" + credentials { + username = System.getenv("GITHUB_ACTOR") + password = System.getenv("GITHUB_TOKEN") + } + } + } } } @@ -116,7 +145,7 @@ tasks.withType(JavaCompile) { check.dependsOn jacocoTestReport jacocoTestReport { reports { - xml.enabled true + xml.required = true html.destination file("${buildDir}/jacocoHtml") } } @@ -128,9 +157,12 @@ javadoc { } } -if (project.hasProperty("signing.keyId")) { +if (project.hasProperty("signingKey")) { apply plugin: "signing" signing { + def signingKey = findProperty("signingKey") + def signingPassword = findProperty("signingPassword") + useInMemoryPgpKeys(signingKey, signingPassword) sign publishing.publications.mavenJava } } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 7454180f2ae8848c63b8b4dea2cb829da983f2fa..943f0cbfa754578e88a3dae77fce6e3dea56edbf 100644 GIT binary patch delta 36900 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

yHlr~Qj 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 GTKgBj+ffbx diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 070cb70..a363877 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip +networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1b6c787..65dcd68 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,10 +80,10 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' @@ -143,12 +143,16 @@ fi if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -205,6 +209,12 @@ set -- \ org.gradle.wrapper.GradleWrapperMain \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index 107acd3..93e3f59 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal From 40059f487c86ab9f10b13c7191556102760a2011 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Fri, 6 Oct 2023 02:11:01 +0300 Subject: [PATCH 56/61] [2.1.0-SNAPSHOT] CI updated --- .github/workflows/publish-release.yml | 49 +++++++++++++++++++ .github/workflows/publish-snapshot.yml | 44 +++++++++++++++++ .../{gradle.yml => pull-request.yml} | 34 +++++++------ 3 files changed, 112 insertions(+), 15 deletions(-) create mode 100644 .github/workflows/publish-release.yml create mode 100644 .github/workflows/publish-snapshot.yml rename .github/workflows/{gradle.yml => pull-request.yml} (54%) diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml new file mode 100644 index 0000000..3aa7884 --- /dev/null +++ b/.github/workflows/publish-release.yml @@ -0,0 +1,49 @@ +name: CI Master + +on: + release: + types: [ published ] + +jobs: + publish-release: + runs-on: ubuntu-latest + name: Publish Release + + steps: + - uses: actions/checkout@v3 + - name: Set up JDK + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'adopt' + + - name: Build + run: './gradlew classes' + + - name: Test + run: './gradlew test jacocoTestReport' + env: + ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY_2 }} + + - name: SonarQube + run: './gradlew sonar --info' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + + - name: Publish Release to GitHub Packages + run: './gradlew publishMavenJavaPublicationToGitHubPackagesRepository' + env: + RELEASE_VERSION: ${{ github.ref_name }} + GITHUB_TOKEN: ${{ secrets.OSS_GITHUB_TOKEN }} + ORG_GRADLE_PROJECT_signingKey: ${{ secrets.OSS_SIGNING_KEY }} + ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.OSS_SIGNING_PASSWORD }} + + - name: Publish Release to OSSRH + run: './gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository' + env: + RELEASE_VERSION: ${{ github.ref_name }} + OSS_USERNAME: ${{ secrets.OSS_USERNAME }} + OSS_PASSWORD: ${{ secrets.OSS_PASSWORD }} + ORG_GRADLE_PROJECT_signingKey: ${{ secrets.OSS_SIGNING_KEY }} + ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.OSS_SIGNING_PASSWORD }} diff --git a/.github/workflows/publish-snapshot.yml b/.github/workflows/publish-snapshot.yml new file mode 100644 index 0000000..d181a6c --- /dev/null +++ b/.github/workflows/publish-snapshot.yml @@ -0,0 +1,44 @@ +name: CI Dev + +on: + push: + paths: + - '**/workflows/*.yml' + - '**/java/**' + - '*.java' + - '*.gradle' + - '*.properties' + branches: + - dev + +jobs: + publish-snapshot: + runs-on: ubuntu-latest + name: Publish Snapshot + + steps: + - uses: actions/checkout@v3 + - name: Set up JDK + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'adopt' + + - name: Code Style + run: './gradlew spotlessCheck' + + - name: Build + run: './gradlew classes' + + - name: Test + run: './gradlew test jacocoTestReport' + env: + ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY_2 }} + + - name: Publish Snapshot + run: './gradlew publish' + env: + OSS_USERNAME: ${{ secrets.OSS_USERNAME }} + OSS_PASSWORD: ${{ secrets.OSS_PASSWORD }} + ORG_GRADLE_PROJECT_signingKey: ${{ secrets.OSS_SIGNING_KEY }} + ORG_GRADLE_PROJECT_signingPassword: ${{ secrets.OSS_SIGNING_PASSWORD }} diff --git a/.github/workflows/gradle.yml b/.github/workflows/pull-request.yml similarity index 54% rename from .github/workflows/gradle.yml rename to .github/workflows/pull-request.yml index 31c42f0..570e503 100644 --- a/.github/workflows/gradle.yml +++ b/.github/workflows/pull-request.yml @@ -1,9 +1,6 @@ -name: Java CI +name: CI Pull Request on: - push: - branches: - - master pull_request: branches: - master @@ -15,37 +12,44 @@ jobs: strategy: matrix: java: [ '11', '17' ] - name: Java ${{ matrix.java }} setup + name: Java ${{ matrix.java }} Pull Request setup steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v3 - name: Set up JDK - uses: actions/setup-java@v1 - + uses: actions/setup-java@v3 with: java-version: ${{ matrix.java }} + distribution: 'adopt' - - name: Build - run: ./gradlew classes + - name: Code Style + run: './gradlew spotlessCheck' - - name: Codestyle - run: ./gradlew spotlessCheck + - name: Build + run: './gradlew classes' - name: Test if: matrix.java == '11' - run: ./gradlew test jacocoTestReport + run: './gradlew test jacocoTestReport' env: ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY_1 }} - name: Test if: matrix.java == '17' - run: ./gradlew test jacocoTestReport + run: './gradlew test jacocoTestReport' env: ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY_2 }} - name: SonarQube if: matrix.java == '17' - run: ./gradlew sonarqube + run: './gradlew sonar --info' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + + - name: Test Report + if: matrix.java == '17' + uses: EnricoMi/publish-unit-test-result-action@v2 + with: + files: | + **/test-results/**/*.xml From 519c26ae22fdc79e76aab423000b76be91bef339 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Fri, 6 Oct 2023 02:15:47 +0300 Subject: [PATCH 57/61] [2.1.0-SNAPSHOT] Javadoc fixed --- src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java b/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java index b6db82e..3f48127 100644 --- a/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java +++ b/src/main/java/io/goodforgod/api/etherscan/StatisticAPI.java @@ -17,7 +17,7 @@ public interface StatisticAPI { /** * ERC20 token total Supply * EtherScan + * "https://docs.etherscan.io/api-endpoints/tokens#get-erc20-token-totalsupply-by-contractaddress">EtherScan * Returns the current amount of an ERC-20 token in circulation. * * @param contract contract address From b6e9ba5e988fefa805db78f10bdac90cb1b98c6e Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Fri, 6 Oct 2023 02:21:15 +0300 Subject: [PATCH 58/61] [2.1.0-SNAPSHOT] Method renamed --- .../java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java | 2 +- src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java | 2 +- src/test/java/io/goodforgod/api/etherscan/ApiRunner.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java b/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java index 70d9a01..2b70711 100644 --- a/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java +++ b/src/main/java/io/goodforgod/api/etherscan/EthScanAPIBuilder.java @@ -89,7 +89,7 @@ public EtherScanAPI.Builder withConverter(@NotNull Supplier converter } @NotNull - public EtherScanAPI.Builder withRetryOnLimitReach(int maxRetryCount) { + public EtherScanAPI.Builder withRetryOnRateLimit(int maxRetryCount) { if (maxRetryCount < 0 || maxRetryCount > 20) { throw new IllegalStateException("maxRetryCount value must be in range from 0 to 20, but was: " + maxRetryCount); } diff --git a/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java index 4e6bc57..bae1902 100644 --- a/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java +++ b/src/main/java/io/goodforgod/api/etherscan/EtherScanAPI.java @@ -71,7 +71,7 @@ interface Builder { * @return self */ @NotNull - EtherScanAPI.Builder withRetryOnLimitReach(@Range(from = 0, to = 20) int maxRetryCount); + EtherScanAPI.Builder withRetryOnRateLimit(@Range(from = 0, to = 20) int maxRetryCount); @NotNull EtherScanAPI build(); diff --git a/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java b/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java index 72aeeff..bc4f334 100644 --- a/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java +++ b/src/test/java/io/goodforgod/api/etherscan/ApiRunner.java @@ -29,7 +29,7 @@ public class ApiRunner extends Assertions { .withApiKey(ApiRunner.API_KEY) .withNetwork(EthNetworks.MAINNET) .withQueue(queueManager) - .withRetryOnLimitReach(5) + .withRetryOnRateLimit(5) .build(); } From c855695fdc61fb978bf0c86669ee476c051915b9 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Fri, 6 Oct 2023 02:21:55 +0300 Subject: [PATCH 59/61] [2.1.0-SNAPSHOT] Method renamed --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 7dcf4c7..7e28207 100644 --- a/build.gradle +++ b/build.gradle @@ -40,7 +40,7 @@ test { reports { html.required = false - junitXml.required = false + junitXml.required = true } environment([ From 3a252b4e32ebc5e4408ca25c903fb3776e5458ac Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Fri, 6 Oct 2023 02:22:51 +0300 Subject: [PATCH 60/61] [2.1.0-SNAPSHOT] CI updated --- .github/workflows/pull-request.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 570e503..0b49f50 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -40,16 +40,16 @@ jobs: env: ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY_2 }} - - name: SonarQube - if: matrix.java == '17' - run: './gradlew sonar --info' - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - - name: Test Report if: matrix.java == '17' uses: EnricoMi/publish-unit-test-result-action@v2 with: files: | **/test-results/**/*.xml + + - name: SonarQube + if: matrix.java == '17' + run: './gradlew sonar --info' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} From 3de7b242b9f65daaaedbec0ef67ea83fd45c0706 Mon Sep 17 00:00:00 2001 From: Anton Kurako Date: Mon, 15 Jan 2024 12:39:58 +0300 Subject: [PATCH 61/61] [2.1.0-SNAPSHOT] README.md updated --- README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index c086a6b..0d06c99 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ API support all Ethereum [default networks](https://docs.etherscan.io/getting-st - [Sepolia](https://api-sepolia.etherscan.io/) ```java -EtherScanAPI api = EtherScanAPI.build(); +EtherScanAPI api = EtherScanAPI.builder().build(); EtherScanAPI apiGoerli = EtherScanAPI.builder().withNetwork(EthNetworks.GORLI).build(); EtherScanAPI apiSepolia = EtherScanAPI.builder().withNetwork(EthNetworks.SEPOLIA).build(); ``` @@ -97,7 +97,7 @@ Below are examples for each API category. **Get Ether Balance for a single Address** ```java -EtherScanAPI api = EtherScanAPI.build(); +EtherScanAPI api = EtherScanAPI.builder().build(); Balance balance = api.account().balance("0x8d4426f94e42f721C7116E81d6688cd935cB3b4F"); ``` @@ -105,14 +105,14 @@ Balance balance = api.account().balance("0x8d4426f94e42f721C7116E81d6688cd935cB3 **Get uncles block for block height** ```java -EtherScanAPI api = EtherScanAPI.build(); +EtherScanAPI api = EtherScanAPI.builder().build(); Optional uncles = api.block().uncles(200000); ``` ### Contract API **Request contract ABI from [verified codes](https://etherscan.io/contractsVerified)** ```java -EtherScanAPI api = EtherScanAPI.build(); +EtherScanAPI api = EtherScanAPI.builder().build(); Abi abi = api.contract().contractAbi("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413"); ``` @@ -120,7 +120,7 @@ Abi abi = api.contract().contractAbi("0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413 **Get event logs for single topic** ```java -EtherScanAPI api = EtherScanAPI.build(); +EtherScanAPI api = EtherScanAPI.builder().build(); LogQuery query = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") .withTopic("0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545") .build(); @@ -129,7 +129,7 @@ List logs = api.logs().logs(query); **Get event logs for 3 topics with respectful operations** ```java -EtherScanAPI api = EtherScanAPI.build(); +EtherScanAPI api = EtherScanAPI.builder().build(); LogQuery query = LogQuery.builder("0x33990122638b9132ca29c723bdf037f1a891a70c") .withBlockFrom(379224) .withBlockTo(400000) @@ -148,13 +148,13 @@ List logs = api.logs().logs(query); **Get tx details with proxy endpoint** ```java -EtherScanAPI api = EtherScanAPI.build(); +EtherScanAPI api = EtherScanAPI.builder().build(); Optional tx = api.proxy().tx("0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1"); ``` **Get block info with proxy endpoint** ```java -EtherScanAPI api = EtherScanAPI.build(); +EtherScanAPI api = EtherScanAPI.builder().build(); Optional block = api.proxy().block(15215); ``` @@ -162,7 +162,7 @@ Optional block = api.proxy().block(15215); **Statistic about last price** ```java -EtherScanAPI api = EtherScanAPI.build(); +EtherScanAPI api = EtherScanAPI.builder().build(); Price price = api.stats().priceLast(); ``` @@ -170,7 +170,7 @@ Price price = api.stats().priceLast(); **Request receipt status for tx** ```java -EtherScanAPI api = EtherScanAPI.build(); +EtherScanAPI api = EtherScanAPI.builder().build(); Optional status = api.txs().receiptStatus("0x513c1ba0bebf66436b5fed86ab668452b7805593c05073eb2d51d3a52f480a76"); ```